react-native-clarity 3.1.1 → 4.0.2

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.
package/README.md CHANGED
@@ -1,50 +1,108 @@
1
+ **⚠️ This package is on the path to deprecation. Please use @microsoft/react-native-clarity instead for new projects or future updates.**
2
+
1
3
  # react-native-clarity
2
4
 
3
- A ReactNative plugin that allows integrating Clarity with your application.
5
+ A React Native plugin that allows integrating Clarity with your application.
6
+
7
+ ## Notes
8
+
9
+ - React Native versions earlier than `0.64` are not supported.
10
+ - **Platform Support**: Android and iOS.
11
+ - iOS support was experimental before version `4.0.1` and required enabling the `enableIOS_experimental` feature flag.
12
+ - The Clarity package depends on native code to run. Therefore, you've to ship a new build after integrating the package.
13
+ - Expo Go does not support plugins requiring native code to execute. Hence, Clarity SDK doesn't operate if you're running through Expo GO. To run the SDK locally, use the [EAS Build](https://docs.expo.dev/build/setup/) system.
4
14
 
5
15
  ## Installation
6
16
 
7
- ```sh
17
+ ### Step 1: Add package dependency
18
+
19
+ To add the package, run the following command.
20
+
21
+ ```bash
8
22
  npm install react-native-clarity
9
23
  ```
10
24
 
11
- ### Notes
25
+ ### Step 2: Initialize Clarity
26
+
27
+ Initialize Clarity by adding the following.
28
+
29
+ ```typescript
30
+ import * as Clarity from 'react-native-clarity';
31
+
32
+ Clarity.initialize('<project-id>', {
33
+ /* logLevel: Clarity.LogLevel.Verbose, */
34
+ });
35
+ ```
36
+
37
+ ### Step 3: Testing
38
+
39
+ After integrating Clarity with your application, ensure these steps for testing on a device or a simulator:
40
+
41
+ 1. Your device or simulator should be connected to the internet.
42
+ 2. The OS version on your device or simulator should be within the supported range specified at [Platform/Framework Support Matrix](https://learn.microsoft.com/en-us/clarity/mobile-sdk/react-native-sdk#platformframework-support-matrix).
43
+ 3. For initial setup, consider setting the log level to Verbose. This provides detailed logs that can help identify any initialization issues.
44
+
45
+ Allow approximately 2 hours for complete sessions to appear in your Clarity project on the Clarity website. However, in-progress sessions can still be viewed in real time. See [Clarity Recordings in Real Time](https://learn.microsoft.com/en-us/clarity/session-recordings/clarity-real-time).
46
+
47
+ ## Known issues
12
48
 
13
- - The Clarity package depends on native code to run, therefore you have to ship a new build after integrating the package.
14
- - **For `react-native-svg` Users**: Clarity is currently incompatible with `react-native-svg` version 13.x due to a known bug that causes user interaction and playback issues. To ensure proper functionality, please upgrade to `react-native-svg` version 14 or later.
49
+ - **For `react-native-svg` Users**: A known issue in `react-native-svg` version 13.x affects user interaction and playback when used with Clarity. Upgrade to `react-native-svg` version 14 or later for proper behavior and compatibility.
15
50
 
16
51
  ## Usage
17
52
 
18
53
  ```js
19
- import { LogLevel, initialize, setCustomUserId, setCustomSessionId, setCustomTag, setCurrentScreenName, getCurrentSessionId, getCurrentSessionUrl } from 'react-native-clarity';
54
+ import * as Clarity from 'react-native-clarity';
20
55
 
21
56
  // Initialize Clarity.
22
57
  const clarityConfig = {
23
- logLevel: LogLevel.Verbose,
24
- allowMeteredNetworkUsage: true
58
+ logLevel: Clarity.LogLevel.Verbose
25
59
  };
26
60
 
27
- initialize('<ProjectId>', clarityConfig);
61
+ Clarity.initialize('<project-id>', clarityConfig);
28
62
 
29
- // Pause Clarity capturing.
30
- pause();
63
+ // Pauses the Clarity session capturing until a call to the 'resume' function is made.
64
+ Clarity.pause();
31
65
 
32
- // Resume Clarity capturing if paused.
33
- resume();
66
+ // Resumes the Clarity session capturing only if it was previously paused by a call to the `pause` function.
67
+ Clarity.resume();
34
68
 
35
- // Returns true if clarity was paused.
36
- isPaused().then((paused) => {...});
69
+ // Checks if Clarity session capturing is currently paused based on an earlier call to the `pause` function.
70
+ Clarity.isPaused().then((paused) => {...});
37
71
 
38
- // Set custom user id.
39
- setCustomUserId("<CustomUserId>");
72
+ // Forces Clarity to start a new session asynchronously.
73
+ Clarity.startNewSession(sessionId => {
74
+ // Sets a custom user ID for the current session. This ID can be used to filter sessions on the Clarity dashboard.
75
+ Clarity.setCustomUserId("<CustomUserId>");
40
76
 
41
- // Set custom session id.
42
- setCustomSessionId("<CustomSessionId>");
77
+ // Sets a custom session ID for the current session. This ID can be used to filter sessions on the Clarity dashboard.
78
+ Clarity.setCustomSessionId("<CustomSessionId>");
43
79
 
44
- // Set custom tag.
45
- setCustomTag("key", "value");
80
+ // Sets a custom tag for the current session. This tag can be used to filter sessions on the Clarity dashboard.
81
+ Clarity.setCustomTag("key", "value");
82
+ });
46
83
 
47
- // Setting the current screen name when using React Navigation
84
+ // Sends a custom event to the current Clarity session. These custom events can be used to track specific user interactions or actions that Clarity's built-in event tracking doesn't capture.
85
+ Clarity.sendCustomEvent('Initialized');
86
+
87
+ // Sets a callback function that's invoked whenever a new Clarity session starts or an existing session is resumed at app startup.
88
+ // If the callback is set after a session has already started, the callback is invoked right away with the current session ID.
89
+ Clarity.setOnSessionStartedCallback(sessionId => { });
90
+
91
+ // Provide a custom screen name tag that's added as a suffix to the base screen name.
92
+ Clarity.setCurrentScreenName('Home');
93
+
94
+ // [DEPRECATED] This function is deprecated and will be removed in a future major version.
95
+ // Returns the ID of the currently active Clarity session if a session has already started; otherwise `null`.
96
+ Clarity.getCurrentSessionId().then((id) => {...});
97
+
98
+ // Returns the URL of the current Clarity session recording on the Clarity dashboard if a session has already started; otherwise `null`.
99
+ Clarity.getCurrentSessionUrl().then((url) => {...});
100
+ ```
101
+
102
+ ### Using Clarity With React Navigation
103
+
104
+ ```js
105
+ import * as Clarity from 'react-native-clarity';
48
106
  import { NavigationContainer useNavigationContainerRef } from '@react-navigation/native';
49
107
 
50
108
  const HomeScreen = ({...}) => {
@@ -56,7 +114,12 @@ const HomeScreen = ({...}) => {
56
114
  ref={navigationRef}
57
115
  onReady={() => {
58
116
  routeNameRef.current = navigationRef.getCurrentRoute().name;
59
- setCurrentScreenName(routeNameRef.current);
117
+ const clarityConfig = {
118
+ logLevel: Clarity.LogLevel.Verbose
119
+ };
120
+
121
+ Clarity.initialize('<project-id>', clarityConfig);
122
+ Clarity.setCurrentScreenName(routeNameRef.current);
60
123
  }}
61
124
  onStateChange={() => {
62
125
  const previousRouteName = routeNameRef.current;
@@ -64,7 +127,7 @@ const HomeScreen = ({...}) => {
64
127
 
65
128
  if (previousRouteName !== currentRouteName) {
66
129
  routeNameRef.current = currentRouteName;
67
- setCurrentScreenName(currentRouteName);
130
+ Clarity.setCurrentScreenName(currentRouteName);
68
131
  }
69
132
  }}
70
133
  >
@@ -72,53 +135,44 @@ const HomeScreen = ({...}) => {
72
135
  </NavigationContainer>
73
136
  );
74
137
  };
75
-
76
- // Get current session id to correlate with other tools.
77
- getCurrentSessionId().then((id) => {...});
78
-
79
- // Get current session url to correlate with other tools.
80
- getCurrentSessionUrl().then((url) => {...});
81
138
  ```
82
139
 
83
140
  ### Initialization arguments
84
141
 
85
142
  ```ts
86
143
  /**
87
- * Initializes the Clarity SDK if the API level is supported.
144
+ * @param projectId - The unique identifier assigned to your Clarity project. You can find it on the **Settings** page of Clarity dashboard. This ID is essential for routing data to the correct Clarity project (required).
145
+ * @param config - Configuration of Clarity that tunes the SDK behavior (e.g., what user ID to use, log level, ...etc) (optional).
88
146
  *
89
- * @param projectId [REQUIRED] The Clarity project id to send data to.
90
- * @param config [OPTIONAL] The clarity config, if not provided default values are used.
91
- */
147
+ * **Notes:**
148
+ * - The initialization function is asynchronous, meaning it returns before Clarity is fully initialized.
149
+ * - For actions that require Clarity to be fully initialized, it's recommended to use the {@linkcode setOnSessionStartedCallback} function.
150
+ */
92
151
  function initialize(projectId: string, config?: ClarityConfig)
93
152
 
94
153
  /**
95
- * The configuration that will be used to customize the Clarity behaviour.
96
- *
97
- * @param userId [OPTIONAL default = null] A custom identifier for the current user. If passed as null, the user id
98
- * will be auto generated. The user id in general is sticky across sessions.
99
- * The provided user id must follow these conditions:
100
- * 1. Cannot be an empty string.
101
- * 2. Should be base36 and smaller than "1Z141Z4".
102
- * @param logLevel [OPTIONAL default = LogLevel.None] The level of logging to show in the device logcat stream.
103
- * @param allowMeteredNetworkUsage [OPTIONAL default = false] Allows uploading session data to the servers on device metered network.
104
- * @param enableWebViewCapture [OPTIONAL default = true] Allows Clarity to capture the web views DOM content.
105
- * @param allowedDomains [OPTIONAL default = ["*"]] The whitelisted domains to allow Clarity to capture their DOM content.
106
- * If it contains "*" as an element, all domains will be captured.
107
- * Note: iOS currently does not support allowedDomains feature.
108
- * @param disableOnLowEndDevices [OPTIONAL default = false] Disable Clarity on low-end devices.
109
- * @param maximumDailyNetworkUsageInMB [OPTIONAL default = null] Maximum daily network usage for Clarity (null = No limit). When the limit is reached, Clarity will turn on lean mode.
110
- * Note: iOS currently does not support limiting network usage.
111
- * @param enableIOS_experimental [OPTIONAL default = false] Experimental flag to enable Clarity on iOS platform.
154
+ * A class that allows you to configure the Clarity SDK behavior.
112
155
  */
113
- interface ClarityConfig {
114
- userId?: string | null;
156
+ export interface ClarityConfig {
157
+ /**
158
+ * @deprecated This property is deprecated and will be removed in a future major version. Please use {@linkcode setCustomSessionId} instead.
159
+ *
160
+ * The unique identifier associated with the application user. This ID persists across multiple sessions on the same device.
161
+ *
162
+ * **Notes:**
163
+ * - If no {@linkcode userId} is provided, a random one will be generated automatically.
164
+ * - Must be a base-36 string smaller than `1Z141Z4`.
165
+ * - If an invalid {@linkcode userId} is supplied:
166
+ * - If no `customUserId` is specified, {@linkcode userId} will be used as a `customUserId`, and a new random {@linkcode userId} will be assigned.
167
+ * - If `customUserId` is specified, the invalid {@linkcode userID} will be ignored.
168
+ * - For more flexibility in user identification, consider using the {@linkcode setCustomUserId} API. However, keep in mind that `customUserId` length must be between 1 and 255 characters.
169
+ */
170
+ userId?: string;
171
+
172
+ /**
173
+ * The level of logging to show in the device’s logging stream while debugging. By default, the SDK logs nothing.
174
+ */
115
175
  logLevel?: LogLevel;
116
- allowMeteredNetworkUsage?: boolean;
117
- enableWebViewCapture?: boolean;
118
- allowedDomains?: string[];
119
- disableOnLowEndDevices?: Boolean;
120
- maximumDailyNetworkUsageInMB?: number;
121
- enableIOS_experimental?: boolean;
122
176
  }
123
177
  ```
124
178
 
@@ -69,7 +69,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
69
69
  dependencies {
70
70
  implementation "com.facebook.react:react-native:+"
71
71
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
72
- implementation "com.microsoft.clarity:clarity:2.4.0"
72
+ implementation "com.microsoft.clarity:clarity:3.0.0"
73
73
  }
74
74
 
75
75
  if (isNewArchitectureEnabled()) {
@@ -0,0 +1,55 @@
1
+ package com.microsoft.clarity.reactnative
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
6
+ import com.facebook.react.bridge.ReactMethod
7
+ import com.facebook.react.modules.core.DeviceEventManagerModule
8
+ import com.microsoft.clarity.Clarity
9
+
10
+ internal class ClarityEmitter(private val reactContext: ReactApplicationContext) :
11
+ ReactContextBaseJavaModule(reactContext) {
12
+
13
+ private var listenerCount = 0
14
+ private var registeredOnSessionStartedCallback = false
15
+
16
+ override fun getName(): String {
17
+ return NAME
18
+ }
19
+
20
+ @ReactMethod
21
+ fun addListener(eventName: String) {
22
+ listenerCount += 1
23
+
24
+ registerOnSessionStartedCallback()
25
+ }
26
+
27
+ @ReactMethod
28
+ fun removeListeners(count: Int) {
29
+ listenerCount -= count
30
+ }
31
+
32
+ private fun registerOnSessionStartedCallback() {
33
+ // `setOnSessionStartedCallback` should be called:
34
+ // - once to maintain the same callback.
35
+ // - only when there are listeners so that they can receive the first invocation from the SDK.
36
+ if (registeredOnSessionStartedCallback) return
37
+
38
+ registeredOnSessionStartedCallback = Clarity.setOnSessionStartedCallback { sessionId ->
39
+ if (listenerCount <= 0) return@setOnSessionStartedCallback
40
+
41
+ val params = Arguments.createMap().apply {
42
+ putString(CLARITY_SESSION_ID_PARAMETER, sessionId)
43
+ }
44
+
45
+ reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
46
+ .emit(CLARITY_EVENT_SESSION_STARTED, params)
47
+ }
48
+ }
49
+
50
+ companion object {
51
+ const val NAME = "ClarityEmitter"
52
+ const val CLARITY_EVENT_SESSION_STARTED = "sessionStarted"
53
+ const val CLARITY_SESSION_ID_PARAMETER = "sessionId"
54
+ }
55
+ }
@@ -20,33 +20,13 @@ class ClarityModule(reactContext: ReactApplicationContext) :
20
20
  projectId: String,
21
21
  userId: String?,
22
22
  logLevel: String,
23
- allowMeteredNetworkUsage: Boolean,
24
- enableWebViewCapture: Boolean,
25
- allowedDomains: ReadableArray,
26
- disableOnLowEndDevices: Boolean,
27
- enableDailyNetworkUsageLimit: Boolean,
28
- maximumDailyNetworkUsageInMB: Double,
29
23
  promise: Promise
30
24
  ) {
31
- val allowedActivities = listOf<String>(); // not supported
32
- val disallowedActivities = listOf<String>(); // not supported
33
-
34
- // We use two parameters because the react method parameters do not accept nullable primitive types.
35
- // Moreover, the Long data type is not supported. Js numbers are translated into doubles.
36
- val maximumDailyNetworkUsageInMBLong = if (enableDailyNetworkUsageLimit) maximumDailyNetworkUsageInMB.toLong() else null
37
-
38
25
  val config = ClarityConfig(
39
26
  projectId,
40
27
  userId,
41
28
  LogLevel.valueOf(logLevel),
42
- allowMeteredNetworkUsage,
43
- enableWebViewCapture,
44
- readableArrayToList(allowedDomains),
45
- ApplicationFramework.ReactNative,
46
- allowedActivities,
47
- disallowedActivities,
48
- disableOnLowEndDevices,
49
- maximumDailyNetworkUsageInMBLong
29
+ ApplicationFramework.ReactNative
50
30
  )
51
31
 
52
32
  // Run on the main thread as recommended by the native Clarity SDK.
@@ -74,6 +54,12 @@ class ClarityModule(reactContext: ReactApplicationContext) :
74
54
  promise.resolve(Clarity.isPaused())
75
55
  }
76
56
 
57
+ @ReactMethod
58
+ fun startNewSession(callback: Callback) {
59
+ Clarity.startNewSession {
60
+ callback.invoke(it)
61
+ }
62
+ }
77
63
 
78
64
  @ReactMethod
79
65
  fun setCustomUserId(customUserId: String, promise: Promise) {
@@ -95,6 +81,11 @@ class ClarityModule(reactContext: ReactApplicationContext) :
95
81
  promise.resolve(Clarity.setCustomTag(key, value))
96
82
  }
97
83
 
84
+ @ReactMethod
85
+ fun sendCustomEvent(value: String, promise: Promise) {
86
+ promise.resolve(Clarity.sendCustomEvent(value))
87
+ }
88
+
98
89
  @ReactMethod
99
90
  fun setCurrentScreenName(screenName: String?, promise: Promise) {
100
91
  promise.resolve(Clarity.setCurrentScreenName(screenName))
@@ -105,7 +96,6 @@ class ClarityModule(reactContext: ReactApplicationContext) :
105
96
  promise.resolve(Clarity.getCurrentSessionUrl())
106
97
  }
107
98
 
108
-
109
99
  private fun readableArrayToList(arr: ReadableArray): List<String> {
110
100
  val ret = mutableListOf<String>()
111
101
 
@@ -8,7 +8,10 @@ import com.facebook.react.uimanager.ViewManager
8
8
 
9
9
  class ClarityPackage : ReactPackage {
10
10
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
11
- return listOf(ClarityModule(reactContext))
11
+ return listOf(
12
+ ClarityModule(reactContext),
13
+ ClarityEmitter(reactContext)
14
+ )
12
15
  }
13
16
 
14
17
  override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
package/ios/Clarity.m CHANGED
@@ -4,17 +4,12 @@
4
4
  @import Clarity;
5
5
 
6
6
  @implementation Clarity
7
+
7
8
  RCT_EXPORT_MODULE()
8
9
 
9
10
  RCT_EXPORT_METHOD(initialize:(NSString *)projectId
10
11
  userId:(NSString *)userId
11
12
  logLevel:(NSString *)logLevel
12
- allowMeteredNetworkUsage:(BOOL)allowMeteredNetworkUsage
13
- enableWebViewCapture:(BOOL)enableWebViewCapture
14
- allowedDomains:(NSArray *)allowedDomains
15
- disableOnLowEndDevices:(BOOL)disableOnLowEndDevices
16
- enableDailyNetworkUsageLimit:(BOOL)enableDailyNetworkUsageLimit
17
- maximumDailyNetworkUsageInMB:(double)maximumDailyNetworkUsageInMB
18
13
  resolver:(RCTPromiseResolveBlock)resolve
19
14
  rejecter:(RCTPromiseRejectBlock)reject)
20
15
  {
@@ -23,29 +18,28 @@ RCT_EXPORT_METHOD(initialize:(NSString *)projectId
23
18
 
24
19
  config.userId = userId;
25
20
  config.logLevel = [self convertLogLevelToEnum:logLevel];
26
- config.allowMeteredNetworkUsage = allowMeteredNetworkUsage;
27
- config.enableWebViewCapture = enableWebViewCapture;
28
- config.disableOnLowEndDevices = disableOnLowEndDevices;
29
21
  config.applicationFramework = ApplicationFrameworkReactNative;
30
22
 
31
- [ClaritySDK initializeWithConfig:config];
23
+ resolve(@([ClaritySDK initializeWithConfig:config]));
32
24
  });
33
-
34
- resolve(@(true));
35
25
  }
36
26
 
37
27
  RCT_EXPORT_METHOD(pause:(RCTPromiseResolveBlock)resolve
38
28
  rejecter:(RCTPromiseRejectBlock)reject)
39
29
  {
40
- [ClaritySDK pause];
41
- resolve(@([ClaritySDK isPaused]));
30
+ dispatch_async(dispatch_get_main_queue(), ^{
31
+ [ClaritySDK pause];
32
+ resolve(@([ClaritySDK isPaused]));
33
+ });
42
34
  }
43
35
 
44
36
  RCT_EXPORT_METHOD(resume:(RCTPromiseResolveBlock)resolve
45
37
  rejecter:(RCTPromiseRejectBlock)reject)
46
38
  {
47
- [ClaritySDK resume];
48
- resolve(@((BOOL)(![ClaritySDK isPaused])));
39
+ dispatch_async(dispatch_get_main_queue(), ^{
40
+ [ClaritySDK resume];
41
+ resolve(@((BOOL)(![ClaritySDK isPaused])));
42
+ });
49
43
  }
50
44
 
51
45
  RCT_EXPORT_METHOD(isPaused:(RCTPromiseResolveBlock)resolve
@@ -54,18 +48,31 @@ RCT_EXPORT_METHOD(isPaused:(RCTPromiseResolveBlock)resolve
54
48
  resolve(@([ClaritySDK isPaused]));
55
49
  }
56
50
 
51
+ RCT_EXPORT_METHOD(startNewSession:(RCTResponseSenderBlock)callback)
52
+ {
53
+ dispatch_async(dispatch_get_main_queue(), ^{
54
+ [ClaritySDK startNewSessionWithCallback:^(NSString *sessionId) {
55
+ callback(@[sessionId]);
56
+ }];
57
+ });
58
+ }
59
+
57
60
  RCT_EXPORT_METHOD(setCustomUserId:(NSString *)customUserId
58
61
  resolver:(RCTPromiseResolveBlock)resolve
59
62
  rejecter:(RCTPromiseRejectBlock)reject)
60
63
  {
61
- resolve(@([ClaritySDK setCustomUserId:customUserId]));
64
+ dispatch_async(dispatch_get_main_queue(), ^{
65
+ resolve(@([ClaritySDK setCustomUserId:customUserId]));
66
+ });
62
67
  }
63
68
 
64
69
  RCT_EXPORT_METHOD(setCustomSessionId:(NSString *)customSessionId
65
70
  resolver:(RCTPromiseResolveBlock)resolve
66
71
  rejecter:(RCTPromiseRejectBlock)reject)
67
72
  {
68
- resolve(@([ClaritySDK setCustomSessionId:customSessionId]));
73
+ dispatch_async(dispatch_get_main_queue(), ^{
74
+ resolve(@([ClaritySDK setCustomSessionId:customSessionId]));
75
+ });
69
76
  }
70
77
 
71
78
  RCT_EXPORT_METHOD(getCurrentSessionId:(RCTPromiseResolveBlock)resolve
@@ -74,6 +81,12 @@ RCT_EXPORT_METHOD(getCurrentSessionId:(RCTPromiseResolveBlock)resolve
74
81
  resolve([ClaritySDK getCurrentSessionId]);
75
82
  }
76
83
 
84
+ RCT_EXPORT_METHOD(getCurrentSessionUrl:(RCTPromiseResolveBlock)resolve
85
+ rejecter:(RCTPromiseRejectBlock)reject)
86
+ {
87
+ resolve([ClaritySDK getCurrentSessionUrl]);
88
+ }
89
+
77
90
  RCT_EXPORT_METHOD(setCustomTag:(NSString *)key value:(NSString *)value
78
91
  resolver:(RCTPromiseResolveBlock)resolve
79
92
  rejecter:(RCTPromiseRejectBlock)reject)
@@ -81,17 +94,20 @@ RCT_EXPORT_METHOD(setCustomTag:(NSString *)key value:(NSString *)value
81
94
  resolve(@([ClaritySDK setCustomTagWithKey:key value:value]));
82
95
  }
83
96
 
84
- RCT_EXPORT_METHOD(setCurrentScreenName:(NSString *)currentScreenName
97
+ RCT_EXPORT_METHOD(sendCustomEvent:(NSString *)value
85
98
  resolver:(RCTPromiseResolveBlock)resolve
86
99
  rejecter:(RCTPromiseRejectBlock)reject)
87
100
  {
88
- resolve(@([ClaritySDK setCurrentScreenNameWithName:currentScreenName]));
101
+ resolve(@([ClaritySDK sendCustomEventWithValue:value]));
89
102
  }
90
103
 
91
- RCT_EXPORT_METHOD(getCurrentSessionUrl:(RCTPromiseResolveBlock)resolve
104
+ RCT_EXPORT_METHOD(setCurrentScreenName:(NSString *)currentScreenName
105
+ resolver:(RCTPromiseResolveBlock)resolve
92
106
  rejecter:(RCTPromiseRejectBlock)reject)
93
107
  {
94
- resolve([ClaritySDK getCurrentSessionUrl]);
108
+ dispatch_async(dispatch_get_main_queue(), ^{
109
+ resolve(@([ClaritySDK setCurrentScreenName:currentScreenName]));
110
+ });
95
111
  }
96
112
 
97
113
  - (LogLevel)convertLogLevelToEnum:(NSString*)logLevel
@@ -8,6 +8,7 @@
8
8
 
9
9
  /* Begin PBXBuildFile section */
10
10
  6F8543912B793D4F00B05BAC /* Clarity.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F8543902B793D4F00B05BAC /* Clarity.m */; };
11
+ 75415BDF2CC97A5E001213D6 /* ClarityEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 75415BDE2CC97A5E001213D6 /* ClarityEmitter.m */; };
11
12
  /* End PBXBuildFile section */
12
13
 
13
14
  /* Begin PBXCopyFilesBuildPhase section */
@@ -26,6 +27,8 @@
26
27
  134814201AA4EA6300B7C361 /* libClarity.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libClarity.a; sourceTree = BUILT_PRODUCTS_DIR; };
27
28
  6F85438F2B793D4F00B05BAC /* Clarity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Clarity.h; sourceTree = "<group>"; };
28
29
  6F8543902B793D4F00B05BAC /* Clarity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Clarity.m; sourceTree = "<group>"; };
30
+ 75415BDD2CC97A5E001213D6 /* ClarityEmitter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClarityEmitter.h; sourceTree = "<group>"; };
31
+ 75415BDE2CC97A5E001213D6 /* ClarityEmitter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ClarityEmitter.m; sourceTree = "<group>"; };
29
32
  /* End PBXFileReference section */
30
33
 
31
34
  /* Begin PBXFrameworksBuildPhase section */
@@ -52,6 +55,8 @@
52
55
  children = (
53
56
  6F85438F2B793D4F00B05BAC /* Clarity.h */,
54
57
  6F8543902B793D4F00B05BAC /* Clarity.m */,
58
+ 75415BDD2CC97A5E001213D6 /* ClarityEmitter.h */,
59
+ 75415BDE2CC97A5E001213D6 /* ClarityEmitter.m */,
55
60
  134814211AA4EA7D00B7C361 /* Products */,
56
61
  );
57
62
  sourceTree = "<group>";
@@ -115,6 +120,7 @@
115
120
  buildActionMask = 2147483647;
116
121
  files = (
117
122
  6F8543912B793D4F00B05BAC /* Clarity.m in Sources */,
123
+ 75415BDF2CC97A5E001213D6 /* ClarityEmitter.m in Sources */,
118
124
  );
119
125
  runOnlyForDeploymentPostprocessing = 0;
120
126
  };
@@ -0,0 +1,6 @@
1
+ #import <React/RCTEventEmitter.h>
2
+
3
+
4
+ @interface ClarityEmitter : RCTEventEmitter <RCTBridgeModule>
5
+
6
+ @end
@@ -0,0 +1,50 @@
1
+ #import "ClarityEmitter.h"
2
+ #import <React/RCTLog.h>
3
+
4
+ @import Clarity;
5
+
6
+ static NSString *const kClarityEventSessionStarted = @"sessionStarted";
7
+ static NSString *const kClaritySessionIdParameter = @"sessionId";
8
+
9
+ @implementation ClarityEmitter
10
+ {
11
+ bool hasListeners;
12
+ bool registeredOnSessionStartedCallback;
13
+ }
14
+
15
+ RCT_EXPORT_MODULE()
16
+
17
+ - (NSArray<NSString *> *)supportedEvents {
18
+ return @[kClarityEventSessionStarted];
19
+ }
20
+
21
+ // Will be called when this module's first listener is added.
22
+ -(void)startObserving {
23
+ hasListeners = YES;
24
+
25
+ [self registerOnSessionStartedCallback];
26
+ }
27
+
28
+ // Will be called when this module's last listener is removed, or on dealloc.
29
+ -(void)stopObserving {
30
+ hasListeners = NO;
31
+ }
32
+
33
+ - (void)registerOnSessionStartedCallback {
34
+ // `setOnSessionStartedCallback` should be called:
35
+ // - once to maintain the same callback.
36
+ // - only when there are listeners so that they can receive the first invocation from the SDK.
37
+ if (self->registeredOnSessionStartedCallback) {
38
+ return;
39
+ }
40
+
41
+ self->registeredOnSessionStartedCallback = [ClaritySDK setOnSessionStartedCallback:^(NSString * _Nonnull sessionId) {
42
+ if (self->hasListeners) { // Only send events if anyone is listening.
43
+ [self sendEventWithName:kClarityEventSessionStarted body:@{
44
+ kClaritySessionIdParameter: sessionId
45
+ }];
46
+ }
47
+ }];
48
+ }
49
+
50
+ @end