@amplitude/plugin-session-replay-react-native 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +66 -0
  3. package/amplitude-plugin-session-replay-react-native.podspec +43 -0
  4. package/android/build.gradle +101 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/AndroidManifestNew.xml +2 -0
  8. package/android/src/main/java/com/amplitude/pluginsessionreplayreactnative/PluginSessionReplayReactNativeModule.kt +78 -0
  9. package/android/src/main/java/com/amplitude/pluginsessionreplayreactnative/PluginSessionReplayReactNativePackage.kt +17 -0
  10. package/android/src/main/java/com/amplitude/pluginsessionreplayreactnative/PluginSessionReplayViewManager.kt +22 -0
  11. package/ios/ConsoleLogger.swift +49 -0
  12. package/ios/PluginSessionReplayReactNative-Bridging-Header.h +2 -0
  13. package/ios/PluginSessionReplayReactNative.mm +17 -0
  14. package/ios/PluginSessionReplayReactNative.swift +54 -0
  15. package/ios/RCTAmpMaskViewManager.m +29 -0
  16. package/lib/commonjs/AmpMaskView.js +10 -0
  17. package/lib/commonjs/AmpMaskView.js.map +1 -0
  18. package/lib/commonjs/index.js +20 -0
  19. package/lib/commonjs/index.js.map +1 -0
  20. package/lib/commonjs/native-module.js +18 -0
  21. package/lib/commonjs/native-module.js.map +1 -0
  22. package/lib/commonjs/session-replay.js +59 -0
  23. package/lib/commonjs/session-replay.js.map +1 -0
  24. package/lib/commonjs/version.js +9 -0
  25. package/lib/commonjs/version.js.map +1 -0
  26. package/lib/module/AmpMaskView.js +3 -0
  27. package/lib/module/AmpMaskView.js.map +1 -0
  28. package/lib/module/index.js +3 -0
  29. package/lib/module/index.js.map +1 -0
  30. package/lib/module/native-module.js +11 -0
  31. package/lib/module/native-module.js.map +1 -0
  32. package/lib/module/session-replay.js +58 -0
  33. package/lib/module/session-replay.js.map +1 -0
  34. package/lib/module/version.js +2 -0
  35. package/lib/module/version.js.map +1 -0
  36. package/lib/typescript/AmpMaskView.d.ts +7 -0
  37. package/lib/typescript/AmpMaskView.d.ts.map +1 -0
  38. package/lib/typescript/index.d.ts +3 -0
  39. package/lib/typescript/index.d.ts.map +1 -0
  40. package/lib/typescript/native-module.d.ts +2 -0
  41. package/lib/typescript/native-module.d.ts.map +1 -0
  42. package/lib/typescript/session-replay.d.ts +12 -0
  43. package/lib/typescript/session-replay.d.ts.map +1 -0
  44. package/lib/typescript/version.d.ts +2 -0
  45. package/lib/typescript/version.d.ts.map +1 -0
  46. package/package.json +96 -0
  47. package/src/AmpMaskView.tsx +8 -0
  48. package/src/index.tsx +3 -0
  49. package/src/native-module.tsx +19 -0
  50. package/src/session-replay.ts +60 -0
  51. package/src/version.ts +1 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Amplitude, Inc.
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @amplitude/plugin-session-replay-react-native
2
+
3
+ Amplitude Session Replay plugin for React Native
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install @amplitude/plugin-session-replay-react-native
9
+ ```
10
+
11
+ ## Usage
12
+ Add the session replay plugin to your Amplitude instance as follows
13
+
14
+ ```js
15
+ import { SessionReplayPlugin } from '@amplitude/plugin-session-replay-react-native';
16
+
17
+ // ...
18
+
19
+ await init('YOUR_API_KEY').promise;
20
+ await add(new SessionReplayPlugin()).promise;
21
+
22
+ ```
23
+
24
+ ## Masking views
25
+ To maks certain views, add the `AmpMaskView` tag with the mask property `amp-mask` around the section to be masked
26
+
27
+ ```js
28
+ import { AmpMaskView } from '@amplitude/plugin-session-replay-react-native';
29
+
30
+ // ...
31
+
32
+ <AmpMaskView mask="amp-mask">
33
+ <Text
34
+ style={[
35
+ styles.sectionTitle,
36
+ {
37
+ color: isDarkMode ? Colors.white : Colors.black,
38
+ },
39
+ ]}
40
+ >
41
+ {title}
42
+ </Text>
43
+ </AmpMaskView>
44
+ ```
45
+
46
+ ## Unmasking views
47
+ To unmask views, add the `AmpMaskView` tag with the mask property `amp-unmask` around the section to be unmasked
48
+
49
+ ```js
50
+ import { AmpMaskView } from '@amplitude/plugin-session-replay-react-native';
51
+
52
+ // ...
53
+
54
+ <AmpMaskView mask="amp-unmask">
55
+ <Text
56
+ style={[
57
+ styles.sectionTitle,
58
+ {
59
+ color: isDarkMode ? Colors.white : Colors.black,
60
+ },
61
+ ]}
62
+ >
63
+ {title}
64
+ </Text>
65
+ </AmpMaskView>
66
+ ```
@@ -0,0 +1,43 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
5
+
6
+ Pod::Spec.new do |s|
7
+ s.name = "amplitude-plugin-session-replay-react-native"
8
+ s.version = package["version"]
9
+ s.summary = package["description"]
10
+ s.homepage = package["homepage"]
11
+ s.license = package["license"]
12
+ s.authors = package["author"]
13
+
14
+ s.platforms = { :ios => min_ios_version_supported }
15
+ s.source = { :git => "https://github.com/amplitude/Amplitude-TypeScript.git", :tag => "#{s.version}" }
16
+
17
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
18
+
19
+ s.dependency 'AmplitudeSessionReplay'
20
+
21
+ # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
22
+ # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
23
+ if respond_to?(:install_modules_dependencies, true)
24
+ install_modules_dependencies(s)
25
+ else
26
+ s.dependency "React-Core"
27
+
28
+ # Don't install the dependencies when we run `pod install` in the old architecture.
29
+ if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
30
+ s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
31
+ s.pod_target_xcconfig = {
32
+ "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
33
+ "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
34
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
35
+ }
36
+ s.dependency "React-Codegen"
37
+ s.dependency "RCT-Folly"
38
+ s.dependency "RCTRequired"
39
+ s.dependency "RCTTypeSafety"
40
+ s.dependency "ReactCommon/turbomodule/core"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,101 @@
1
+ buildscript {
2
+ // Buildscript is evaluated before everything else so we can't use getExtOrDefault
3
+ def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["PluginSessionReplayReactNative_kotlinVersion"]
4
+
5
+ repositories {
6
+ google()
7
+ mavenCentral()
8
+ }
9
+
10
+ dependencies {
11
+ classpath "com.android.tools.build:gradle:7.2.1"
12
+ // noinspection DifferentKotlinGradleVersion
13
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
+ }
15
+ }
16
+
17
+ def reactNativeArchitectures() {
18
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
19
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
20
+ }
21
+
22
+ def isNewArchitectureEnabled() {
23
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
24
+ }
25
+
26
+ apply plugin: "com.android.library"
27
+ apply plugin: "kotlin-android"
28
+
29
+ if (isNewArchitectureEnabled()) {
30
+ apply plugin: "com.facebook.react"
31
+ }
32
+
33
+ def getExtOrDefault(name) {
34
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["PluginSessionReplayReactNative_" + name]
35
+ }
36
+
37
+ def getExtOrIntegerDefault(name) {
38
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["PluginSessionReplayReactNative_" + name]).toInteger()
39
+ }
40
+
41
+ def supportsNamespace() {
42
+ def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
43
+ def major = parsed[0].toInteger()
44
+ def minor = parsed[1].toInteger()
45
+
46
+ // Namespace support was added in 7.3.0
47
+ return (major == 7 && minor >= 3) || major >= 8
48
+ }
49
+
50
+ android {
51
+ if (supportsNamespace()) {
52
+ namespace "com.amplitude.pluginsessionreplayreactnative"
53
+
54
+ sourceSets {
55
+ main {
56
+ manifest.srcFile "src/main/AndroidManifestNew.xml"
57
+ }
58
+ }
59
+ }
60
+
61
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
62
+
63
+ defaultConfig {
64
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
65
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
66
+
67
+ }
68
+
69
+ buildTypes {
70
+ release {
71
+ minifyEnabled false
72
+ }
73
+ }
74
+
75
+ lintOptions {
76
+ disable "GradleCompatible"
77
+ }
78
+
79
+ compileOptions {
80
+ sourceCompatibility JavaVersion.VERSION_1_8
81
+ targetCompatibility JavaVersion.VERSION_1_8
82
+ }
83
+ }
84
+
85
+ repositories {
86
+ mavenCentral()
87
+ google()
88
+ }
89
+
90
+ def kotlin_version = getExtOrDefault("kotlinVersion")
91
+
92
+ dependencies {
93
+ implementation("com.amplitude:session-replay-android:[0.15.2, 1.0.0]")
94
+
95
+ // For < 0.71, this will be from the local maven repo
96
+ // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
97
+ //noinspection GradleDynamicVersion
98
+ implementation "com.facebook.react:react-native:+"
99
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
100
+ }
101
+
@@ -0,0 +1,5 @@
1
+ PluginSessionReplayReactNative_kotlinVersion=1.7.0
2
+ PluginSessionReplayReactNative_minSdkVersion=21
3
+ PluginSessionReplayReactNative_targetSdkVersion=31
4
+ PluginSessionReplayReactNative_compileSdkVersion=31
5
+ PluginSessionReplayReactNative_ndkversion=21.4.7075529
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.amplitude.pluginsessionreplayreactnative">
3
+ </manifest>
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,78 @@
1
+ package com.amplitude.pluginsessionreplayreactnative
2
+
3
+ import com.amplitude.android.sessionreplay.SessionReplay
4
+ import com.amplitude.common.Logger
5
+ import com.amplitude.common.android.LogcatLogger
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
8
+ import com.facebook.react.bridge.ReactMethod
9
+ import com.facebook.react.bridge.Promise
10
+ import com.facebook.react.bridge.WritableMap
11
+ import com.facebook.react.bridge.WritableNativeMap
12
+
13
+ class PluginSessionReplayReactNativeModule(private val reactContext: ReactApplicationContext) :
14
+ ReactContextBaseJavaModule(reactContext) {
15
+ private lateinit var sessionReplay: SessionReplay
16
+
17
+ override fun getName(): String {
18
+ return "PluginSessionReplayReactNative"
19
+ }
20
+
21
+ @ReactMethod
22
+ fun setup(apiKey: String, deviceId: String?, sessionId: Double) {
23
+ LogcatLogger.logger.logMode = Logger.LogMode.DEBUG
24
+ sessionReplay = SessionReplay(
25
+ apiKey,
26
+ reactContext.applicationContext,
27
+ deviceId ?: "",
28
+ sessionId.toLong(),
29
+ logger = LogcatLogger.logger,
30
+ sampleRate = 1.0,
31
+ enableRemoteConfig = false,
32
+ )
33
+ }
34
+
35
+ @ReactMethod
36
+ fun setSessionId(sessionId: Double) {
37
+ sessionReplay.setSessionId(sessionId.toLong())
38
+ }
39
+
40
+ @ReactMethod
41
+ fun getSessionId(promise: Promise) {
42
+ promise.resolve(sessionReplay.getSessionId().toDouble())
43
+ }
44
+
45
+ @ReactMethod
46
+ fun getSessionReplayProperties(promise: Promise) {
47
+ val properties: Map<String, Any> = sessionReplay.getSessionReplayProperties()
48
+ val map: WritableMap = WritableNativeMap()
49
+ for ((key, value) in properties) {
50
+ if (value is String) {
51
+ map.putString(key, value)
52
+ } else if (value is Int) {
53
+ map.putInt(key, value)
54
+ } else if (value is Long) {
55
+ map.putDouble(key, value.toDouble())
56
+ } else if (value is Double) {
57
+ map.putDouble(key, value)
58
+ } else if (value is Boolean) {
59
+ map.putBoolean(key, value)
60
+ }
61
+ }
62
+ promise.resolve(map)
63
+ }
64
+
65
+ @ReactMethod
66
+ fun flush() {
67
+ sessionReplay.flush()
68
+ }
69
+
70
+ @ReactMethod
71
+ fun teardown() {
72
+ sessionReplay.shutdown()
73
+ }
74
+
75
+ override fun invalidate() {
76
+ sessionReplay.shutdown()
77
+ }
78
+ }
@@ -0,0 +1,17 @@
1
+ package com.amplitude.pluginsessionreplayreactnative
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+
9
+ class PluginSessionReplayReactNativePackage : ReactPackage {
10
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
11
+ return listOf(PluginSessionReplayReactNativeModule(reactContext))
12
+ }
13
+
14
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
15
+ return listOf(PluginSessionReplayViewManager())
16
+ }
17
+ }
@@ -0,0 +1,22 @@
1
+ package com.amplitude.pluginsessionreplayreactnative
2
+
3
+ import android.view.ViewGroup
4
+ import com.amplitude.android.sessionreplay.SessionReplay
5
+ import com.facebook.react.uimanager.annotations.ReactProp
6
+ import com.facebook.react.views.view.ReactViewManager
7
+
8
+ class PluginSessionReplayViewManager : ReactViewManager() {
9
+
10
+ override fun getName(): String {
11
+ return "RCTAmpMaskView"
12
+ }
13
+
14
+ @ReactProp(name = "mask")
15
+ fun setMask(view: ViewGroup, ampMask: String) {
16
+ when (ampMask) {
17
+ "amp-mask" -> SessionReplay.mask(view)
18
+ "amp-unmask" -> SessionReplay.unmask(view)
19
+ "amp-block" -> SessionReplay.block(view)
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,49 @@
1
+ import Foundation
2
+ import os.log
3
+ import AmplitudeSessionReplay
4
+
5
+
6
+ @objc(AMPLogLevel)
7
+ public enum LogLevelEnum: Int {
8
+ case OFF
9
+ case ERROR
10
+ case WARN
11
+ case LOG
12
+ case DEBUG
13
+ }
14
+
15
+ public class ConsoleLogger: AmplitudeSessionReplay.Logger {
16
+ public typealias LogLevel = LogLevelEnum
17
+
18
+ public var logLevel: Int
19
+ private var logger: OSLog
20
+
21
+ public init(logLevel: Int = LogLevelEnum.OFF.rawValue) {
22
+ self.logLevel = logLevel
23
+ self.logger = OSLog(subsystem: "Amplitude", category: "Logging")
24
+ }
25
+
26
+ public func error(message: String) {
27
+ if logLevel >= LogLevel.ERROR.rawValue {
28
+ os_log("Error: %@", log: logger, type: .error, message)
29
+ }
30
+ }
31
+
32
+ public func warn(message: String) {
33
+ if logLevel >= LogLevel.WARN.rawValue {
34
+ os_log("Warn: %@", log: logger, type: .default, message)
35
+ }
36
+ }
37
+
38
+ public func log(message: String) {
39
+ if logLevel >= LogLevel.LOG.rawValue {
40
+ os_log("Log: %@", log: logger, type: .info, message)
41
+ }
42
+ }
43
+
44
+ public func debug(message: String) {
45
+ if logLevel >= LogLevel.DEBUG.rawValue {
46
+ os_log("Debug: %@", log: logger, type: .debug, message)
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,2 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTViewManager.h>
@@ -0,0 +1,17 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface RCT_EXTERN_MODULE(PluginSessionReplayReactNative, NSObject)
4
+
5
+ RCT_EXTERN_METHOD(setup:(NSString)apiKey deviceId:(NSString)deviceId sessionId:(nonnull NSNumber)sessionId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
6
+
7
+ RCT_EXTERN_METHOD(setSessionId:(nonnull NSNumber) resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
8
+
9
+ RCT_EXTERN_METHOD(getSessionId:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
10
+
11
+ RCT_EXTERN_METHOD(getSessionReplayProperties:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
12
+
13
+ RCT_EXTERN_METHOD(flush:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
14
+
15
+ RCT_EXTERN_METHOD(teardown:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
16
+
17
+ @end
@@ -0,0 +1,54 @@
1
+ import Foundation
2
+ import AmplitudeSessionReplay
3
+
4
+ @objc(PluginSessionReplayReactNative)
5
+ class PluginSessionReplayReactNative: NSObject {
6
+
7
+ var sessionReplay: SessionReplay!
8
+
9
+ @objc(setup:deviceId:sessionId:resolve:reject:)
10
+ func setup(_ apiKey: String, deviceId: String, sessionId: NSNumber, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
11
+ print("setup: \(apiKey) \(deviceId) \(sessionId)")
12
+ sessionReplay = SessionReplay(apiKey:apiKey,
13
+ deviceId: deviceId,
14
+ sessionId: sessionId.int64Value,
15
+ sampleRate: 1.0,
16
+ logger:ConsoleLogger(logLevel: LogLevelEnum.DEBUG.rawValue),
17
+ enableRemoteConfig: false)
18
+ sessionReplay.start()
19
+ resolve(nil)
20
+ }
21
+
22
+ @objc(setSessionId:resolve:reject:)
23
+ func setSessionId(_ sessionId: NSNumber, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
24
+ print("setSessionId: \(sessionId)")
25
+ sessionReplay.sessionId = sessionId.int64Value
26
+ resolve(nil)
27
+ }
28
+
29
+ @objc(getSessionId:reject:)
30
+ func getSessionId(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
31
+ print("getSessionId")
32
+ resolve(NSNumber(value:sessionReplay.sessionId))
33
+ }
34
+
35
+ @objc(getSessionReplayProperties:reject:)
36
+ func getSessionReplayProperties(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
37
+ print("getSessionReplayProperties")
38
+ resolve(sessionReplay.additionalEventProperties)
39
+ }
40
+
41
+ @objc(flush:reject:)
42
+ func flush(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
43
+ print("flush")
44
+ sessionReplay.flush()
45
+ resolve(nil)
46
+ }
47
+
48
+ @objc(teardown:reject:)
49
+ func teardown(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
50
+ print("teardown")
51
+ sessionReplay.stop()
52
+ resolve(nil)
53
+ }
54
+ }
@@ -0,0 +1,29 @@
1
+ #import <React/RCTViewManager.h>
2
+ #import <React/RCTView.h>
3
+ @import AmplitudeSessionReplay;
4
+
5
+ @interface RCTAmpMaskViewManager : RCTViewManager
6
+ @end
7
+
8
+ @implementation RCTAmpMaskViewManager
9
+
10
+ RCT_EXPORT_MODULE(RCTAmpMaskView)
11
+
12
+ - (UIView *)view
13
+ {
14
+ return [[RCTView alloc] init];
15
+ }
16
+
17
+ RCT_CUSTOM_VIEW_PROPERTY(mask, NSString, RCTView)
18
+ {
19
+ NSString* mask = [RCTConvert NSString:json];
20
+ if ([mask isEqualToString:@"amp-mask"]) {
21
+ view.amp_isBlocked = true;
22
+ } else if ([mask isEqualToString:@"amp-block"]) {
23
+ view.amp_isBlocked = true;
24
+ } else if ([mask isEqualToString:@"amp-unmask"]) {
25
+ view.amp_isBlocked = false;
26
+ }
27
+ }
28
+
29
+ @end
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.AmpMaskView = void 0;
7
+ var _reactNative = require("react-native");
8
+ const AmpMaskView = (0, _reactNative.requireNativeComponent)('RCTAmpMaskView');
9
+ exports.AmpMaskView = AmpMaskView;
10
+ //# sourceMappingURL=AmpMaskView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["AmpMaskView","requireNativeComponent"],"sourceRoot":"../../src","sources":["AmpMaskView.tsx"],"mappings":";;;;;;AAAA;AAMO,MAAMA,WAAW,GACtB,IAAAC,mCAAsB,EAAmB,gBAAgB,CAAC;AAAC"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "AmpMaskView", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _AmpMaskView.AmpMaskView;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "SessionReplayPlugin", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _sessionReplay.SessionReplayPlugin;
16
+ }
17
+ });
18
+ var _sessionReplay = require("./session-replay");
19
+ var _AmpMaskView = require("./AmpMaskView");
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AAEA"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.PluginSessionReplayReactNative = void 0;
7
+ var _reactNative = require("react-native");
8
+ const LINKING_ERROR = `The package '@amplitude/plugin-session-replay-react-native' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
9
+ ios: "- You have run 'pod install'\n",
10
+ default: ''
11
+ }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
12
+ const PluginSessionReplayReactNative = _reactNative.NativeModules.PluginSessionReplayReactNative ? _reactNative.NativeModules.PluginSessionReplayReactNative : new Proxy({}, {
13
+ get() {
14
+ throw new Error(LINKING_ERROR);
15
+ }
16
+ });
17
+ exports.PluginSessionReplayReactNative = PluginSessionReplayReactNative;
18
+ //# sourceMappingURL=native-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["LINKING_ERROR","Platform","select","ios","default","PluginSessionReplayReactNative","NativeModules","Proxy","get","Error"],"sourceRoot":"../../src","sources":["native-module.tsx"],"mappings":";;;;;;AAAA;AAEA,MAAMA,aAAa,GAChB,wGAAuG,GACxGC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAE1B,MAAMC,8BAA8B,GACzCC,0BAAa,CAACD,8BAA8B,GACxCC,0BAAa,CAACD,8BAA8B,GAC5C,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAG,GAAG;IACJ,MAAM,IAAIC,KAAK,CAACT,aAAa,CAAC;EAChC;AACF,CAAC,CACF;AAAC"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SessionReplayPlugin = void 0;
7
+ var _nativeModule = require("./native-module");
8
+ var _version = require("./version");
9
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
10
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
11
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
12
+ class SessionReplayPlugin {
13
+ // this.config is defined in setup() which will always be called first
14
+
15
+ // @ts-ignore
16
+
17
+ constructor() {
18
+ _defineProperty(this, "name", '@amplitude/plugin-session-replay-react-native');
19
+ _defineProperty(this, "type", 'enrichment');
20
+ _defineProperty(this, "config", void 0);
21
+ } // empty default constructor
22
+
23
+ async setup(config, _) {
24
+ this.config = config;
25
+ console.log(`Installing @amplitude/plugin-session-replay-react-native, version ${_version.VERSION}.`);
26
+ await _nativeModule.PluginSessionReplayReactNative.setup(config.apiKey, config.deviceId, config.sessionId);
27
+ }
28
+ async execute(event) {
29
+ // On event, synchronize the session id to the what's on the browserConfig (source of truth)
30
+ // Choosing not to read from event object here, concerned about offline/delayed events messing up the state stored
31
+ // in SR.
32
+ if (this.config.sessionId && this.config.sessionId !== (await _nativeModule.PluginSessionReplayReactNative.getSessionId())) {
33
+ await _nativeModule.PluginSessionReplayReactNative.setSessionId(this.config.sessionId);
34
+ }
35
+ // Treating config.sessionId as source of truth, if the event's session id doesn't match, the
36
+ // event is not of the current session (offline/late events). In that case, don't tag the events
37
+ if (this.config.sessionId && this.config.sessionId === event.session_id) {
38
+ const sessionRecordingProperties = await _nativeModule.PluginSessionReplayReactNative.getSessionReplayProperties();
39
+ event.event_properties = {
40
+ ...event.event_properties,
41
+ ...sessionRecordingProperties
42
+ };
43
+ }
44
+ return Promise.resolve(event);
45
+ }
46
+ async teardown() {
47
+ await _nativeModule.PluginSessionReplayReactNative.teardown();
48
+ // the following are initialized in setup() which will always be called first
49
+ // here we reset them to null to prevent memory leaks
50
+
51
+ // @ts-ignore
52
+ this.config = null;
53
+ }
54
+ async getSessionReplayProperties() {
55
+ return _nativeModule.PluginSessionReplayReactNative.getSessionReplayProperties();
56
+ }
57
+ }
58
+ exports.SessionReplayPlugin = SessionReplayPlugin;
59
+ //# sourceMappingURL=session-replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["SessionReplayPlugin","constructor","setup","config","_","console","log","VERSION","PluginSessionReplayReactNative","apiKey","deviceId","sessionId","execute","event","getSessionId","setSessionId","session_id","sessionRecordingProperties","getSessionReplayProperties","event_properties","Promise","resolve","teardown"],"sourceRoot":"../../src","sources":["session-replay.ts"],"mappings":";;;;;;AAOA;AACA;AAAoC;AAAA;AAAA;AAE7B,MAAMA,mBAAmB,CAAmE;EAGjG;;EAEA;;EAGAC,WAAW,GAAG;IAAA,8BAPP,+CAA+C;IAAA,8BAC/C,YAAY;IAAA;EAQnB,CAAC,CADC;;EAGF,MAAMC,KAAK,CAACC,MAAyB,EAAEC,CAAoB,EAAiB;IAC1E,IAAI,CAACD,MAAM,GAAGA,MAAM;IACpBE,OAAO,CAACC,GAAG,CAAE,qEAAoEC,gBAAQ,GAAE,CAAC;IAC5F,MAAMC,4CAA8B,CAACN,KAAK,CAACC,MAAM,CAACM,MAAM,EAAEN,MAAM,CAACO,QAAQ,EAAEP,MAAM,CAACQ,SAAS,CAAC;EAC9F;EAEA,MAAMC,OAAO,CAACC,KAAY,EAAyB;IACjD;IACA;IACA;IACA,IAAI,IAAI,CAACV,MAAM,CAACQ,SAAS,IAAI,IAAI,CAACR,MAAM,CAACQ,SAAS,MAAM,MAAMH,4CAA8B,CAACM,YAAY,EAAE,CAAC,EAAE;MAC5G,MAAMN,4CAA8B,CAACO,YAAY,CAAC,IAAI,CAACZ,MAAM,CAACQ,SAAS,CAAC;IAC1E;IACA;IACA;IACA,IAAI,IAAI,CAACR,MAAM,CAACQ,SAAS,IAAI,IAAI,CAACR,MAAM,CAACQ,SAAS,KAAKE,KAAK,CAACG,UAAU,EAAE;MACvE,MAAMC,0BAA0B,GAAG,MAAMT,4CAA8B,CAACU,0BAA0B,EAAE;MACpGL,KAAK,CAACM,gBAAgB,GAAG;QACvB,GAAGN,KAAK,CAACM,gBAAgB;QACzB,GAAGF;MACL,CAAC;IACH;IACA,OAAOG,OAAO,CAACC,OAAO,CAACR,KAAK,CAAC;EAC/B;EAEA,MAAMS,QAAQ,GAAkB;IAC9B,MAAMd,4CAA8B,CAACc,QAAQ,EAAE;IAC/C;IACA;;IAEA;IACA,IAAI,CAACnB,MAAM,GAAG,IAAI;EACpB;EAEA,MAAMe,0BAA0B,GAAG;IACjC,OAAOV,4CAA8B,CAACU,0BAA0B,EAAE;EACpE;AACF;AAAC"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.VERSION = void 0;
7
+ const VERSION = '0.2.0';
8
+ exports.VERSION = VERSION;
9
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["VERSION"],"sourceRoot":"../../src","sources":["version.ts"],"mappings":";;;;;;AAAO,MAAMA,OAAO,GAAG,OAAO;AAAC"}
@@ -0,0 +1,3 @@
1
+ import { requireNativeComponent } from 'react-native';
2
+ export const AmpMaskView = requireNativeComponent('RCTAmpMaskView');
3
+ //# sourceMappingURL=AmpMaskView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["requireNativeComponent","AmpMaskView"],"sourceRoot":"../../src","sources":["AmpMaskView.tsx"],"mappings":"AAAA,SAASA,sBAAsB,QAAwB,cAAc;AAMrE,OAAO,MAAMC,WAAW,GACtBD,sBAAsB,CAAmB,gBAAgB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { SessionReplayPlugin } from './session-replay';
2
+ export { AmpMaskView } from './AmpMaskView';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["SessionReplayPlugin","AmpMaskView"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,kBAAkB;AAEtD,SAASC,WAAW,QAAQ,eAAe"}
@@ -0,0 +1,11 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+ const LINKING_ERROR = `The package '@amplitude/plugin-session-replay-react-native' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
3
+ ios: "- You have run 'pod install'\n",
4
+ default: ''
5
+ }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
6
+ export const PluginSessionReplayReactNative = NativeModules.PluginSessionReplayReactNative ? NativeModules.PluginSessionReplayReactNative : new Proxy({}, {
7
+ get() {
8
+ throw new Error(LINKING_ERROR);
9
+ }
10
+ });
11
+ //# sourceMappingURL=native-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeModules","Platform","LINKING_ERROR","select","ios","default","PluginSessionReplayReactNative","Proxy","get","Error"],"sourceRoot":"../../src","sources":["native-module.tsx"],"mappings":"AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AAEtD,MAAMC,aAAa,GAChB,wGAAuG,GACxGD,QAAQ,CAACE,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,OAAO,MAAMC,8BAA8B,GACzCN,aAAa,CAACM,8BAA8B,GACxCN,aAAa,CAACM,8BAA8B,GAC5C,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAG,GAAG;IACJ,MAAM,IAAIC,KAAK,CAACP,aAAa,CAAC;EAChC;AACF,CAAC,CACF"}
@@ -0,0 +1,58 @@
1
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
2
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
3
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
4
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
5
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
6
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
7
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
8
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
9
+
10
+ import { PluginSessionReplayReactNative } from './native-module';
11
+ import { VERSION } from './version';
12
+ export class SessionReplayPlugin {
13
+ // this.config is defined in setup() which will always be called first
14
+
15
+ // @ts-ignore
16
+
17
+ constructor() {
18
+ _defineProperty(this, "name", '@amplitude/plugin-session-replay-react-native');
19
+ _defineProperty(this, "type", 'enrichment');
20
+ _defineProperty(this, "config", void 0);
21
+ } // empty default constructor
22
+
23
+ async setup(config, _) {
24
+ this.config = config;
25
+ console.log(`Installing @amplitude/plugin-session-replay-react-native, version ${VERSION}.`);
26
+ await PluginSessionReplayReactNative.setup(config.apiKey, config.deviceId, config.sessionId);
27
+ }
28
+ async execute(event) {
29
+ // On event, synchronize the session id to the what's on the browserConfig (source of truth)
30
+ // Choosing not to read from event object here, concerned about offline/delayed events messing up the state stored
31
+ // in SR.
32
+ if (this.config.sessionId && this.config.sessionId !== (await PluginSessionReplayReactNative.getSessionId())) {
33
+ await PluginSessionReplayReactNative.setSessionId(this.config.sessionId);
34
+ }
35
+ // Treating config.sessionId as source of truth, if the event's session id doesn't match, the
36
+ // event is not of the current session (offline/late events). In that case, don't tag the events
37
+ if (this.config.sessionId && this.config.sessionId === event.session_id) {
38
+ const sessionRecordingProperties = await PluginSessionReplayReactNative.getSessionReplayProperties();
39
+ event.event_properties = {
40
+ ...event.event_properties,
41
+ ...sessionRecordingProperties
42
+ };
43
+ }
44
+ return Promise.resolve(event);
45
+ }
46
+ async teardown() {
47
+ await PluginSessionReplayReactNative.teardown();
48
+ // the following are initialized in setup() which will always be called first
49
+ // here we reset them to null to prevent memory leaks
50
+
51
+ // @ts-ignore
52
+ this.config = null;
53
+ }
54
+ async getSessionReplayProperties() {
55
+ return PluginSessionReplayReactNative.getSessionReplayProperties();
56
+ }
57
+ }
58
+ //# sourceMappingURL=session-replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["PluginSessionReplayReactNative","VERSION","SessionReplayPlugin","constructor","setup","config","_","console","log","apiKey","deviceId","sessionId","execute","event","getSessionId","setSessionId","session_id","sessionRecordingProperties","getSessionReplayProperties","event_properties","Promise","resolve","teardown"],"sourceRoot":"../../src","sources":["session-replay.ts"],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;;AAGA,SAASA,8BAA8B,QAAQ,iBAAiB;AAChE,SAASC,OAAO,QAAQ,WAAW;AAEnC,OAAO,MAAMC,mBAAmB,CAAmE;EAGjG;;EAEA;;EAGAC,WAAW,GAAG;IAAA,8BAPP,+CAA+C;IAAA,8BAC/C,YAAY;IAAA;EAQnB,CAAC,CADC;;EAGF,MAAMC,KAAK,CAACC,MAAyB,EAAEC,CAAoB,EAAiB;IAC1E,IAAI,CAACD,MAAM,GAAGA,MAAM;IACpBE,OAAO,CAACC,GAAG,CAAE,qEAAoEP,OAAQ,GAAE,CAAC;IAC5F,MAAMD,8BAA8B,CAACI,KAAK,CAACC,MAAM,CAACI,MAAM,EAAEJ,MAAM,CAACK,QAAQ,EAAEL,MAAM,CAACM,SAAS,CAAC;EAC9F;EAEA,MAAMC,OAAO,CAACC,KAAY,EAAyB;IACjD;IACA;IACA;IACA,IAAI,IAAI,CAACR,MAAM,CAACM,SAAS,IAAI,IAAI,CAACN,MAAM,CAACM,SAAS,MAAM,MAAMX,8BAA8B,CAACc,YAAY,EAAE,CAAC,EAAE;MAC5G,MAAMd,8BAA8B,CAACe,YAAY,CAAC,IAAI,CAACV,MAAM,CAACM,SAAS,CAAC;IAC1E;IACA;IACA;IACA,IAAI,IAAI,CAACN,MAAM,CAACM,SAAS,IAAI,IAAI,CAACN,MAAM,CAACM,SAAS,KAAKE,KAAK,CAACG,UAAU,EAAE;MACvE,MAAMC,0BAA0B,GAAG,MAAMjB,8BAA8B,CAACkB,0BAA0B,EAAE;MACpGL,KAAK,CAACM,gBAAgB,GAAG;QACvB,GAAGN,KAAK,CAACM,gBAAgB;QACzB,GAAGF;MACL,CAAC;IACH;IACA,OAAOG,OAAO,CAACC,OAAO,CAACR,KAAK,CAAC;EAC/B;EAEA,MAAMS,QAAQ,GAAkB;IAC9B,MAAMtB,8BAA8B,CAACsB,QAAQ,EAAE;IAC/C;IACA;;IAEA;IACA,IAAI,CAACjB,MAAM,GAAG,IAAI;EACpB;EAEA,MAAMa,0BAA0B,GAAG;IACjC,OAAOlB,8BAA8B,CAACkB,0BAA0B,EAAE;EACpE;AACF"}
@@ -0,0 +1,2 @@
1
+ export const VERSION = '0.2.0';
2
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["VERSION"],"sourceRoot":"../../src","sources":["version.ts"],"mappings":"AAAA,OAAO,MAAMA,OAAO,GAAG,OAAO"}
@@ -0,0 +1,7 @@
1
+ import { type ViewProps } from 'react-native';
2
+ interface AmpMaskViewProps extends ViewProps {
3
+ mask: 'amp-mask' | 'amp-unmask' | 'amp-block';
4
+ }
5
+ export declare const AmpMaskView: import("react-native").HostComponent<AmpMaskViewProps>;
6
+ export {};
7
+ //# sourceMappingURL=AmpMaskView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AmpMaskView.d.ts","sourceRoot":"","sources":["../../src/AmpMaskView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEtE,UAAU,gBAAiB,SAAQ,SAAS;IAC1C,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,WAAW,CAAC;CAC/C;AAED,eAAO,MAAM,WAAW,wDACoC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { SessionReplayPlugin } from './session-replay';
2
+ export { AmpMaskView } from './AmpMaskView';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const PluginSessionReplayReactNative: any;
2
+ //# sourceMappingURL=native-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-module.d.ts","sourceRoot":"","sources":["../../src/native-module.tsx"],"names":[],"mappings":"AAQA,eAAO,MAAM,8BAA8B,KAUpC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { EnrichmentPlugin, Event, ReactNativeClient, ReactNativeConfig } from '@amplitude/analytics-types';
2
+ export declare class SessionReplayPlugin implements EnrichmentPlugin<ReactNativeClient, ReactNativeConfig> {
3
+ name: string;
4
+ type: "enrichment";
5
+ config: ReactNativeConfig;
6
+ constructor();
7
+ setup(config: ReactNativeConfig, _: ReactNativeClient): Promise<void>;
8
+ execute(event: Event): Promise<Event | null>;
9
+ teardown(): Promise<void>;
10
+ getSessionReplayProperties(): Promise<any>;
11
+ }
12
+ //# sourceMappingURL=session-replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAKhH,qBAAa,mBAAoB,YAAW,gBAAgB,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAChG,IAAI,SAAmD;IACvD,IAAI,eAAyB;IAI7B,MAAM,EAAE,iBAAiB,CAAC;;IAMpB,KAAK,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrE,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAmB5C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IASzB,0BAA0B;CAGjC"}
@@ -0,0 +1,2 @@
1
+ export declare const VERSION = "0.2.0";
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC"}
package/package.json ADDED
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "@amplitude/plugin-session-replay-react-native",
3
+ "version": "0.2.0",
4
+ "description": "Amplitude Session Replay plugin for React Native",
5
+ "keywords": [
6
+ "analytics",
7
+ "amplitude",
8
+ "react-native",
9
+ "ios",
10
+ "android",
11
+ "session-replay"
12
+ ],
13
+ "author": "Amplitude Inc",
14
+ "homepage": "https://github.com/amplitude/Amplitude-TypeScript",
15
+ "license": "MIT",
16
+ "main": "lib/commonjs/index",
17
+ "module": "lib/module/index",
18
+ "types": "lib/typescript/index.d.ts",
19
+ "react-native": "src/index",
20
+ "publishConfig": {
21
+ "access": "public",
22
+ "tag": "latest"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/amplitude/Amplitude-TypeScript.git"
27
+ },
28
+ "scripts": {
29
+ "build": "bob build",
30
+ "clean": "rimraf node_modules lib coverage",
31
+ "fix": "yarn fix:eslint & yarn fix:prettier",
32
+ "fix:eslint": "eslint '{src,test}/**/*.ts' --fix",
33
+ "fix:prettier": "prettier --write \"{src,test}/**/*.ts\"",
34
+ "lint": "yarn lint:eslint & yarn lint:prettier",
35
+ "lint:eslint": "eslint '{src,test}/**/*.ts'",
36
+ "lint:prettier": "prettier --check \"{src,test}/**/*.ts\"",
37
+ "test": "jest",
38
+ "typecheck": "tsc -p ./tsconfig.json",
39
+ "version": "yarn version-file && yarn build",
40
+ "version-file": "node -p \"'export const VERSION = \\'' + require('./package.json').version + '\\';'\" > src/version.ts",
41
+ "typescript": "tsc --noEmit",
42
+ "example": "yarn workspace @amplitude/plugin-session-replay-react-native-example",
43
+ "pods": "cd example && pod-install --quiet"
44
+ },
45
+ "source": "src/index",
46
+ "files": [
47
+ "src",
48
+ "lib",
49
+ "android",
50
+ "ios",
51
+ "cpp",
52
+ "*.podspec",
53
+ "!ios/build",
54
+ "!android/build",
55
+ "!android/gradle",
56
+ "!android/gradlew",
57
+ "!android/gradlew.bat",
58
+ "!android/local.properties",
59
+ "!test",
60
+ "!**/__tests__",
61
+ "!**/__fixtures__",
62
+ "!**/__mocks__",
63
+ "!**/.*"
64
+ ],
65
+ "bugs": {
66
+ "url": "https://github.com/amplitude/Amplitude-TypeScript/issues"
67
+ },
68
+ "dependencies": {
69
+ "@amplitude/analytics-types": "^1.3.4"
70
+ },
71
+ "devDependencies": {
72
+ "@types/react": "^18.0.26",
73
+ "react": "18.2.0",
74
+ "react-native": "0.73.0",
75
+ "react-native-builder-bob": "^0.20.3"
76
+ },
77
+ "peerDependencies": {
78
+ "react": "*",
79
+ "react-native": "*"
80
+ },
81
+ "react-native-builder-bob": {
82
+ "source": "src",
83
+ "output": "lib",
84
+ "targets": [
85
+ "commonjs",
86
+ "module",
87
+ [
88
+ "typescript",
89
+ {
90
+ "project": "tsconfig.build.json"
91
+ }
92
+ ]
93
+ ]
94
+ },
95
+ "gitHead": "2b09117a76f23d80de58d636e59911a2fda5d169"
96
+ }
@@ -0,0 +1,8 @@
1
+ import { requireNativeComponent, type ViewProps } from 'react-native';
2
+
3
+ interface AmpMaskViewProps extends ViewProps {
4
+ mask: 'amp-mask' | 'amp-unmask' | 'amp-block';
5
+ }
6
+
7
+ export const AmpMaskView =
8
+ requireNativeComponent<AmpMaskViewProps>('RCTAmpMaskView');
package/src/index.tsx ADDED
@@ -0,0 +1,3 @@
1
+ export { SessionReplayPlugin } from './session-replay';
2
+
3
+ export { AmpMaskView } from './AmpMaskView';
@@ -0,0 +1,19 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ const LINKING_ERROR =
4
+ `The package '@amplitude/plugin-session-replay-react-native' doesn't seem to be linked. Make sure: \n\n` +
5
+ Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
6
+ '- You rebuilt the app after installing the package\n' +
7
+ '- You are not using Expo Go\n';
8
+
9
+ export const PluginSessionReplayReactNative =
10
+ NativeModules.PluginSessionReplayReactNative
11
+ ? NativeModules.PluginSessionReplayReactNative
12
+ : new Proxy(
13
+ {},
14
+ {
15
+ get() {
16
+ throw new Error(LINKING_ERROR);
17
+ },
18
+ }
19
+ );
@@ -0,0 +1,60 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
4
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
5
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
6
+ import type { EnrichmentPlugin, Event, ReactNativeClient, ReactNativeConfig } from '@amplitude/analytics-types';
7
+
8
+ import { PluginSessionReplayReactNative } from './native-module';
9
+ import { VERSION } from './version';
10
+
11
+ export class SessionReplayPlugin implements EnrichmentPlugin<ReactNativeClient, ReactNativeConfig> {
12
+ name = '@amplitude/plugin-session-replay-react-native';
13
+ type = 'enrichment' as const;
14
+ // this.config is defined in setup() which will always be called first
15
+
16
+ // @ts-ignore
17
+ config: ReactNativeConfig;
18
+
19
+ constructor() {
20
+ // empty default constructor
21
+ }
22
+
23
+ async setup(config: ReactNativeConfig, _: ReactNativeClient): Promise<void> {
24
+ this.config = config;
25
+ console.log(`Installing @amplitude/plugin-session-replay-react-native, version ${VERSION}.`);
26
+ await PluginSessionReplayReactNative.setup(config.apiKey, config.deviceId, config.sessionId);
27
+ }
28
+
29
+ async execute(event: Event): Promise<Event | null> {
30
+ // On event, synchronize the session id to the what's on the browserConfig (source of truth)
31
+ // Choosing not to read from event object here, concerned about offline/delayed events messing up the state stored
32
+ // in SR.
33
+ if (this.config.sessionId && this.config.sessionId !== (await PluginSessionReplayReactNative.getSessionId())) {
34
+ await PluginSessionReplayReactNative.setSessionId(this.config.sessionId);
35
+ }
36
+ // Treating config.sessionId as source of truth, if the event's session id doesn't match, the
37
+ // event is not of the current session (offline/late events). In that case, don't tag the events
38
+ if (this.config.sessionId && this.config.sessionId === event.session_id) {
39
+ const sessionRecordingProperties = await PluginSessionReplayReactNative.getSessionReplayProperties();
40
+ event.event_properties = {
41
+ ...event.event_properties,
42
+ ...sessionRecordingProperties,
43
+ };
44
+ }
45
+ return Promise.resolve(event);
46
+ }
47
+
48
+ async teardown(): Promise<void> {
49
+ await PluginSessionReplayReactNative.teardown();
50
+ // the following are initialized in setup() which will always be called first
51
+ // here we reset them to null to prevent memory leaks
52
+
53
+ // @ts-ignore
54
+ this.config = null;
55
+ }
56
+
57
+ async getSessionReplayProperties() {
58
+ return PluginSessionReplayReactNative.getSessionReplayProperties();
59
+ }
60
+ }
package/src/version.ts ADDED
@@ -0,0 +1 @@
1
+ export const VERSION = '0.2.0';