react-native-userleap 2.20.0 → 3.0.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.
@@ -70,11 +70,12 @@ repositories {
70
70
  }
71
71
  dependencies {
72
72
  implementation 'com.facebook.react:react-native:+' // From node_modules
73
-
73
+ implementation "androidx.webkit:webkit:1.8.0"
74
+
74
75
  // Use conditional logic for local vs Maven
75
76
  if (findProject(':userleap-android-sdk') != null) {
76
77
  implementation project(':userleap-android-sdk')
77
78
  } else {
78
- implementation ("com.userleap:userleap-android-sdk:2.22.1") // update this version on android updates
79
+ implementation ("com.userleap:userleap-android-sdk:2.23.0") // update this version on android updates
79
80
  }
80
81
  }
@@ -1,20 +1,31 @@
1
1
  package com.userleap.reactnative;
2
2
 
3
3
  import android.app.Activity;
4
+ import android.util.Log;
4
5
 
5
6
  import androidx.fragment.app.FragmentActivity;
6
7
 
8
+ import com.facebook.react.bridge.Arguments;
7
9
  import com.facebook.react.bridge.Callback;
8
10
  import com.facebook.react.bridge.ReactApplicationContext;
9
11
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
10
12
  import com.facebook.react.bridge.ReactMethod;
11
13
  import com.facebook.react.bridge.ReadableArray;
12
14
  import com.facebook.react.bridge.ReadableMap;
13
-
15
+ import com.facebook.react.bridge.WritableMap;
16
+ import com.userleap.EventListener;
17
+ import com.userleap.EventName;
18
+ import com.userleap.EventPayload;
19
+ import com.userleap.SprigEvent;
20
+ import com.userleap.SprigSurveyResult;
14
21
  import com.userleap.SurveyState;
15
22
  import com.userleap.UserLeap;
16
23
 
24
+ import org.json.JSONException;
25
+ import org.json.JSONObject;
26
+
17
27
  import java.util.HashMap;
28
+ import java.util.Iterator;
18
29
  import java.util.List;
19
30
  import java.util.Map;
20
31
  import java.util.Objects;
@@ -25,19 +36,10 @@ import javax.annotation.Nullable;
25
36
  import kotlin.Unit;
26
37
  import kotlin.jvm.functions.Function1;
27
38
 
28
- import android.util.Log;
29
-
30
- import com.userleap.EventListener;
31
- import com.userleap.EventName;
32
- import com.userleap.SprigEvent;
33
- import com.userleap.SprigLoggingLevel;
34
- import com.userleap.UserLeap;
35
-
36
- import kotlin.Unit;
37
- import kotlin.jvm.functions.Function1;
38
-
39
39
  public class UserLeapModule extends ReactContextBaseJavaModule {
40
40
 
41
+ private static final String TAG = "UserLeapModule";
42
+
41
43
  private final ReactApplicationContext reactContext;
42
44
 
43
45
  public UserLeapModule(ReactApplicationContext reactContext) {
@@ -50,6 +52,115 @@ public class UserLeapModule extends ReactContextBaseJavaModule {
50
52
  return "UserLeapBindings";
51
53
  }
52
54
 
55
+ // ---------------------------------------------------------------------------
56
+ // Bridge helpers
57
+ // ---------------------------------------------------------------------------
58
+
59
+ /**
60
+ * Converts UPPER_SNAKE_CASE to camelCase to match the iOS event naming
61
+ * convention expected by the JS layer.
62
+ * e.g. "SURVEY_WILL_PRESENT" -> "surveyWillPresent"
63
+ */
64
+ private String toCamelCase(String upperSnakeCase) {
65
+ String[] parts = upperSnakeCase.toLowerCase().split("_");
66
+ StringBuilder sb = new StringBuilder(parts[0]);
67
+ for (int i = 1; i < parts.length; i++) {
68
+ sb.append(Character.toUpperCase(parts[i].charAt(0)));
69
+ sb.append(parts[i].substring(1));
70
+ }
71
+ return sb.toString();
72
+ }
73
+
74
+ /**
75
+ * Shallow-converts a JSONObject to a WritableMap for passing across the RN bridge.
76
+ */
77
+ private WritableMap jsonObjectToWritableMap(@Nullable JSONObject json) {
78
+ WritableMap map = Arguments.createMap();
79
+ if (json == null) {
80
+ return map;
81
+ }
82
+ Iterator<String> keys = json.keys();
83
+ while (keys.hasNext()) {
84
+ String key = keys.next();
85
+ try {
86
+ Object value = json.get(key);
87
+ if (value instanceof String) {
88
+ map.putString(key, (String) value);
89
+ } else if (value instanceof Boolean) {
90
+ map.putBoolean(key, (Boolean) value);
91
+ } else if (value instanceof Integer) {
92
+ map.putInt(key, (Integer) value);
93
+ } else if (value instanceof Double) {
94
+ map.putDouble(key, (Double) value);
95
+ } else if (value instanceof JSONObject) {
96
+ map.putMap(key, jsonObjectToWritableMap((JSONObject) value));
97
+ } else if (value == JSONObject.NULL) {
98
+ map.putNull(key);
99
+ } else {
100
+ map.putString(key, value.toString());
101
+ }
102
+ } catch (JSONException e) {
103
+ Log.w(TAG, "Failed to convert key '" + key + "' from SprigEvent data", e);
104
+ }
105
+ }
106
+ return map;
107
+ }
108
+
109
+ /**
110
+ * Converts a SprigSurveyResult to a WritableMap for passing across the RN bridge.
111
+ * Returns { surveyState: string, surveyId: int }
112
+ */
113
+ private WritableMap surveyResultToWritableMap(SprigSurveyResult surveyResult) {
114
+ WritableMap resultMap = Arguments.createMap();
115
+ resultMap.putString("surveyState", surveyResult.getSurveyState().name());
116
+ if (surveyResult.getSurveyId() != null) {
117
+ resultMap.putInt("surveyId", surveyResult.getSurveyId());
118
+ } else {
119
+ resultMap.putInt("surveyId", 0);
120
+ }
121
+ return resultMap;
122
+ }
123
+
124
+ /**
125
+ * Wraps a RN Callback as a Function1<SprigSurveyResult, Unit> for the EventPayload API.
126
+ * Returns null if the callback is null.
127
+ */
128
+ private @Nullable Function1<SprigSurveyResult, Unit> wrapResultCallback(@Nullable final Callback callback) {
129
+ if (callback == null) {
130
+ return null;
131
+ }
132
+ return new Function1<SprigSurveyResult, Unit>() {
133
+ @Override
134
+ public Unit invoke(SprigSurveyResult surveyResult) {
135
+ callback.invoke(surveyResultToWritableMap(surveyResult));
136
+ return null;
137
+ }
138
+ };
139
+ }
140
+
141
+ private Map<String, String> stringifyMap(ReadableMap map) {
142
+ Map<String, String> stringifiedMap = new HashMap<>();
143
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
144
+ stringifiedMap = map.toHashMap().entrySet().stream()
145
+ .filter(m -> m.getKey() != null && m.getValue() != null)
146
+ .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toString()));
147
+ }
148
+ return stringifiedMap;
149
+ }
150
+
151
+ private @Nullable FragmentActivity getFragmentActivity() {
152
+ Activity activity = getCurrentActivity();
153
+ if (activity instanceof FragmentActivity) {
154
+ return (FragmentActivity) activity;
155
+ } else {
156
+ return null;
157
+ }
158
+ }
159
+
160
+ // ---------------------------------------------------------------------------
161
+ // SDK properties
162
+ // ---------------------------------------------------------------------------
163
+
53
164
  @ReactMethod(isBlockingSynchronousMethod = true)
54
165
  public int visitorIdentifier() {
55
166
  final Integer visitorIdentifierObject = UserLeap.INSTANCE.getVisitorIdentifier();
@@ -67,15 +178,16 @@ public class UserLeapModule extends ReactContextBaseJavaModule {
67
178
  return UserLeap.INSTANCE.getSdkVersion();
68
179
  }
69
180
 
181
+ // ---------------------------------------------------------------------------
182
+ // Configuration
183
+ // ---------------------------------------------------------------------------
184
+
70
185
  @ReactMethod
71
186
  public void configure(String environment, ReadableMap configuration) {
72
-
73
187
  UserLeap.INSTANCE.addEventListener(EventName.LOGGING_EVENT, new EventListener() {
74
188
  @Override
75
189
  public void onEvent(SprigEvent event) {
76
-
77
190
  String tag = "SprigLoggingEvent";
78
-
79
191
  switch (event.getLogLevel()) {
80
192
  case INFO:
81
193
  Log.i(tag, event.getLogMessage());
@@ -95,13 +207,17 @@ public class UserLeapModule extends ReactContextBaseJavaModule {
95
207
  });
96
208
 
97
209
  UserLeap.INSTANCE.configure(
98
- reactContext,
99
- environment,
100
- stringifyMap(configuration),
101
- getFragmentActivity()
210
+ reactContext,
211
+ environment,
212
+ stringifyMap(configuration),
213
+ getFragmentActivity()
102
214
  );
103
215
  }
104
216
 
217
+ // ---------------------------------------------------------------------------
218
+ // Deprecated track methods (return SurveyState string)
219
+ // ---------------------------------------------------------------------------
220
+
105
221
  @ReactMethod
106
222
  public void track(String event, final Callback surveyStateCallback) {
107
223
  trackAndIdentify(event, null, null, surveyStateCallback);
@@ -109,19 +225,17 @@ public class UserLeapModule extends ReactContextBaseJavaModule {
109
225
 
110
226
  @ReactMethod
111
227
  public void trackWithProperties(String event, String userId, String partnerAnonymousId, ReadableMap properties, final Callback surveyStateCallback) {
112
- if (surveyStateCallback == null) {
228
+ if (surveyStateCallback == null) {
113
229
  UserLeap.INSTANCE.track(event, userId, partnerAnonymousId, stringifyMap(properties));
114
- } else {
230
+ } else {
115
231
  UserLeap.INSTANCE.track(event, userId, partnerAnonymousId, stringifyMap(properties), new Function1<SurveyState, Unit>() {
116
232
  @Override
117
233
  public Unit invoke(SurveyState surveyState) {
118
- if (surveyStateCallback!=null) {
119
- surveyStateCallback.invoke(surveyState.name());
120
- }
234
+ surveyStateCallback.invoke(surveyState.name());
121
235
  return null;
122
236
  }
123
- });
124
- }
237
+ });
238
+ }
125
239
  }
126
240
 
127
241
  @ReactMethod
@@ -139,50 +253,46 @@ public class UserLeapModule extends ReactContextBaseJavaModule {
139
253
  }
140
254
  }
141
255
 
142
- @ReactMethod
143
- public void setEmailAddress(String emailAddress) {
144
- UserLeap.INSTANCE.setEmailAddress(emailAddress);
145
- }
146
-
147
- @ReactMethod
148
- public void setVisitorAttribute(String key, String value) {
149
- UserLeap.INSTANCE.setVisitorAttribute(key, value);
150
- }
151
-
152
- @ReactMethod
153
- public void setVisitorAttributes(ReadableMap attributes) {
154
- UserLeap.INSTANCE.setVisitorAttributes(stringifyMap(attributes));
155
- }
156
-
157
- @ReactMethod
158
- public void setVisitorAttributesAndIdentify(ReadableMap attributes, String userId, String partnerAnonymousId) {
159
- UserLeap.INSTANCE.setVisitorAttributes(stringifyMap(attributes), userId, partnerAnonymousId);
160
- }
256
+ // ---------------------------------------------------------------------------
257
+ // New track methods (return SprigSurveyResult via EventPayload)
258
+ // ---------------------------------------------------------------------------
161
259
 
162
260
  @ReactMethod
163
- public void removeVisitorAttributes(ReadableArray attributes) {
164
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
165
- List<String> attributeStrings = attributes.toArrayList().stream()
166
- .map(object -> Objects.toString(object, null))
167
- .collect(Collectors.toList());
168
- UserLeap.INSTANCE.removeVisitorAttributes(attributeStrings);
169
- }
261
+ public void trackEvent(String event, final Callback surveyStateCallback) {
262
+ trackEventAndIdentify(event, null, null, surveyStateCallback);
170
263
  }
171
264
 
172
265
  @ReactMethod
173
- public void setPreviewKey(String previewKey) {
174
- UserLeap.INSTANCE.setPreviewKey(previewKey);
266
+ public void trackEventWithProperties(String event, String userId, String partnerAnonymousId, ReadableMap properties, final Callback surveyStateCallback) {
267
+ EventPayload payload = new EventPayload(
268
+ event,
269
+ userId,
270
+ partnerAnonymousId,
271
+ stringifyMap(properties),
272
+ wrapResultCallback(surveyStateCallback),
273
+ null, // shouldShowSurveyCallback
274
+ null // deprecated callback
275
+ );
276
+ UserLeap.INSTANCE.track(payload);
175
277
  }
176
278
 
177
279
  @ReactMethod
178
- public void setUserIdentifier(String identifier) {
179
- UserLeap.INSTANCE.setUserIdentifier(identifier);
280
+ public void trackEventAndIdentify(String event, String userId, String partnerAnonymousId, final Callback surveyStateCallback) {
281
+ EventPayload payload = new EventPayload(
282
+ event,
283
+ userId,
284
+ partnerAnonymousId,
285
+ null, // properties
286
+ wrapResultCallback(surveyStateCallback),
287
+ null, // shouldShowSurveyCallback
288
+ null // deprecated callback
289
+ );
290
+ UserLeap.INSTANCE.track(payload);
180
291
  }
181
292
 
182
- @ReactMethod
183
- public void logout() {
184
- UserLeap.INSTANCE.logout();
185
- }
293
+ // ---------------------------------------------------------------------------
294
+ // Track and present
295
+ // ---------------------------------------------------------------------------
186
296
 
187
297
  @ReactMethod
188
298
  public void trackAndPresent(String event) {
@@ -194,6 +304,10 @@ public class UserLeapModule extends ReactContextBaseJavaModule {
194
304
  UserLeap.INSTANCE.trackAndPresent(event, userId, partnerAnonymousId, getFragmentActivity());
195
305
  }
196
306
 
307
+ // ---------------------------------------------------------------------------
308
+ // Survey presentation
309
+ // ---------------------------------------------------------------------------
310
+
197
311
  @ReactMethod
198
312
  public void presentSurvey() {
199
313
  UserLeap.INSTANCE.presentSurvey(null);
@@ -214,23 +328,52 @@ public class UserLeapModule extends ReactContextBaseJavaModule {
214
328
  UserLeap.INSTANCE.unpauseDisplayingSurveys();
215
329
  }
216
330
 
217
- private @Nullable
218
- FragmentActivity getFragmentActivity() {
219
- Activity activity = getCurrentActivity();
220
- if (activity instanceof FragmentActivity) {
221
- return (FragmentActivity) activity;
222
- } else {
223
- return null;
224
- }
331
+ // ---------------------------------------------------------------------------
332
+ // User identity & attributes
333
+ // ---------------------------------------------------------------------------
334
+
335
+ @ReactMethod
336
+ public void setEmailAddress(String emailAddress) {
337
+ UserLeap.INSTANCE.setEmailAddress(emailAddress);
225
338
  }
226
339
 
227
- private Map<String, String> stringifyMap(ReadableMap map) {
228
- Map<String,String> stringifiedMap = new HashMap<>();
340
+ @ReactMethod
341
+ public void setVisitorAttribute(String key, String value) {
342
+ UserLeap.INSTANCE.setVisitorAttribute(key, value);
343
+ }
344
+
345
+ @ReactMethod
346
+ public void setVisitorAttributes(ReadableMap attributes) {
347
+ UserLeap.INSTANCE.setVisitorAttributes(stringifyMap(attributes));
348
+ }
349
+
350
+ @ReactMethod
351
+ public void setVisitorAttributesAndIdentify(ReadableMap attributes, String userId, String partnerAnonymousId) {
352
+ UserLeap.INSTANCE.setVisitorAttributes(stringifyMap(attributes), userId, partnerAnonymousId);
353
+ }
354
+
355
+ @ReactMethod
356
+ public void removeVisitorAttributes(ReadableArray attributes) {
229
357
  if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
230
- stringifiedMap = map.toHashMap().entrySet().stream()
231
- .filter(m -> m.getKey() != null && m.getValue() !=null)
232
- .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toString()));
358
+ List<String> attributeStrings = attributes.toArrayList().stream()
359
+ .map(object -> Objects.toString(object, null))
360
+ .collect(Collectors.toList());
361
+ UserLeap.INSTANCE.removeVisitorAttributes(attributeStrings);
233
362
  }
234
- return stringifiedMap;
235
363
  }
236
- }
364
+
365
+ @ReactMethod
366
+ public void setPreviewKey(String previewKey) {
367
+ UserLeap.INSTANCE.setPreviewKey(previewKey);
368
+ }
369
+
370
+ @ReactMethod
371
+ public void setUserIdentifier(String identifier) {
372
+ UserLeap.INSTANCE.setUserIdentifier(identifier);
373
+ }
374
+
375
+ @ReactMethod
376
+ public void logout() {
377
+ UserLeap.INSTANCE.logout();
378
+ }
379
+ }
package/index.d.ts CHANGED
@@ -4,15 +4,43 @@ declare namespace UserLeap {
4
4
  DISABLED: string;
5
5
  NO_SURVEY: string;
6
6
  };
7
+
7
8
  function visitorIdentifier(): number;
8
9
  function visitorIdentifierString(): string;
9
10
  function sdkVersion(): string;
10
11
  function configure(environment: string, configuration?: {[key: string]: any}): void;
11
12
  function setPreviewKey(previewKey: string): void;
12
13
  function presentSurvey(): void;
14
+ /**
15
+ * @deprecated This function is outdated. Use `trackEvent()` instead.
16
+ */
13
17
  function track(event: string, surveyStateCallback: ((surveyState: string) => void)): void;
18
+ /**
19
+ * @deprecated This function is outdated. Use `trackEventWithProperties()` instead.
20
+ */
14
21
  function trackWithProperties(event: string, userId: string | undefined, partnerAnonymousId: string | undefined, properties: {[key: string]: any}, surveyStateCallback: ((surveyState: string) => void)): void;
22
+ /**
23
+ * @deprecated This function is outdated. Use `trackEventAndIdentify()` instead.
24
+ */
15
25
  function trackAndIdentify(event: string, userId: string | undefined, partnerAnonymousId: string | undefined, surveyStateCallback: ((surveyState: string) => void)): void;
26
+ function trackEvent(
27
+ event: string,
28
+ surveyResultCallback: (result: { surveyState: string; surveyId: number }) => void
29
+ ): void;
30
+ function trackEventWithProperties(
31
+ event: string,
32
+ userId: string | undefined,
33
+ partnerAnonymousId: string | undefined,
34
+ properties: { [key: string]: any },
35
+ surveyResultCallback: (result: { surveyState: string; surveyId: number }) => void
36
+ ): void;
37
+
38
+ function trackEventAndIdentify(
39
+ event: string,
40
+ userId: string | undefined,
41
+ partnerAnonymousId: string | undefined,
42
+ surveyResultCallback: (result: { surveyState: string; surveyId: number }) => void
43
+ ): void;
16
44
  function setEmailAddress(emailAddress: string): void;
17
45
  function setVisitorAttribute(key: string, value: string): void;
18
46
  function setVisitorAttributes(attributes: {[key: string]: string}): void;
@@ -25,8 +53,5 @@ declare namespace UserLeap {
25
53
  function unpauseDisplayingSurveys(): void;
26
54
  function trackAndPresent(event: string): void;
27
55
  function trackIdentifyAndPresent(event: string, userId: string | undefined, partnerAnonymousId: string | undefined): void;
28
- // Uncomment for UI testing
29
- // function renderSnapshotForUITest(name: string, maskingLevel: string)
30
-
31
56
  }
32
57
  export default UserLeap;
package/index.js CHANGED
@@ -8,6 +8,7 @@ const SurveyState = {
8
8
  NO_SURVEY: "NO_SURVEY",
9
9
  };
10
10
 
11
+
11
12
  const stringifyAttributes = (attributes) => {
12
13
  if (!(attributes instanceof Object && attributes.constructor === Object))
13
14
  return {};
@@ -21,7 +22,7 @@ const isValidPlatform = () => {
21
22
  //Platform.Version is a number for android and string for ios
22
23
  return (
23
24
  (Platform.OS === "android" && String(Platform.Version) >= "21") ||
24
- (Platform.OS === "ios" && String(Platform.Version) >= "10.3")
25
+ (Platform.OS === "ios" && String(Platform.Version) >= "15.0")
25
26
  );
26
27
  };
27
28
 
@@ -35,7 +36,7 @@ const visitorIdentifier = () => {
35
36
 
36
37
  const sdkVersion = () => {
37
38
  if (Platform.OS === "ios") {
38
- if (String(Platform.Version) >= "10.3") {
39
+ if (String(Platform.Version) >= "15.0") {
39
40
  return NativeModules.UserLeapBindings.sdkVersion();
40
41
  }
41
42
  } else {
@@ -81,12 +82,18 @@ const presentSurvey = () => {
81
82
  }
82
83
  };
83
84
 
85
+ /**
86
+ * @deprecated This function is outdated. Use `trackEvent()` instead.
87
+ */
84
88
  const track = (event, surveyStateCallback) => {
85
89
  if (isValidPlatform()) {
86
90
  NativeModules.UserLeapBindings.track(event, surveyStateCallback);
87
91
  }
88
92
  };
89
93
 
94
+ /**
95
+ * @deprecated This function is outdated. Use `trackEventWithProperties()` instead.
96
+ */
90
97
  const trackWithProperties = (
91
98
  event,
92
99
  userId,
@@ -105,6 +112,9 @@ const trackWithProperties = (
105
112
  }
106
113
  };
107
114
 
115
+ /**
116
+ * @deprecated This function is outdated. Use `trackEventAndIdentify()` instead.
117
+ */
108
118
  const trackAndIdentify = (
109
119
  event,
110
120
  userId,
@@ -121,6 +131,46 @@ const trackAndIdentify = (
121
131
  }
122
132
  };
123
133
 
134
+ const trackEvent = (event, surveyResultCallback) => {
135
+ if (isValidPlatform()) {
136
+ NativeModules.UserLeapBindings.trackEvent(event, surveyResultCallback);
137
+ }
138
+ };
139
+
140
+ const trackEventWithProperties = (
141
+ event,
142
+ userId,
143
+ partnerAnonymousId,
144
+ properties,
145
+ surveyResultCallback
146
+ ) => {
147
+ if (isValidPlatform()) {
148
+ NativeModules.UserLeapBindings.trackEventWithProperties(
149
+ event,
150
+ userId,
151
+ partnerAnonymousId,
152
+ properties,
153
+ surveyResultCallback
154
+ );
155
+ }
156
+ };
157
+
158
+ const trackEventAndIdentify = (
159
+ event,
160
+ userId,
161
+ partnerAnonynmousId,
162
+ surveyResultCallback
163
+ ) => {
164
+ if (isValidPlatform()) {
165
+ NativeModules.UserLeapBindings.trackEventAndIdentify(
166
+ event,
167
+ userId,
168
+ partnerAnonynmousId,
169
+ surveyResultCallback
170
+ );
171
+ }
172
+ };
173
+
124
174
  const setEmailAddress = (emailAddress) => {
125
175
  if (isValidPlatform()) {
126
176
  NativeModules.UserLeapBindings.setEmailAddress(emailAddress);
@@ -207,15 +257,7 @@ const trackIdentifyAndPresent = (event, userId, partnerAnonynmousId) => {
207
257
  }
208
258
  };
209
259
 
210
- // Uncomment for UI testing
211
- // const renderSnapshotForUITest = (name, maskingLevel) => {
212
- // if (isValidPlatform()) {
213
- // NativeModules.UserLeapBindings.renderSnapshotForUITest(name, maskingLevel);
214
- // }
215
- // }
216
-
217
260
  const UserLeap = {
218
- SurveyState,
219
261
  visitorIdentifier,
220
262
  visitorIdentifierString,
221
263
  sdkVersion,
@@ -225,6 +267,9 @@ const UserLeap = {
225
267
  track,
226
268
  trackWithProperties,
227
269
  trackAndIdentify,
270
+ trackEvent,
271
+ trackEventWithProperties,
272
+ trackEventAndIdentify,
228
273
  setEmailAddress,
229
274
  setVisitorAttribute,
230
275
  setVisitorAttributes,
@@ -237,8 +282,8 @@ const UserLeap = {
237
282
  dismissActiveSurvey,
238
283
  pauseDisplayingSurveys,
239
284
  unpauseDisplayingSurveys,
240
- // Uncomment for UI testing
241
- // renderSnapshotForUITest,
242
285
  };
243
286
 
287
+ UserLeap.SurveyState = SurveyState;
288
+
244
289
  export default UserLeap;
@@ -18,6 +18,13 @@
18
18
  return dispatch_get_main_queue();
19
19
  }
20
20
 
21
+ -(NSDictionary *) parseSurveyResult:(SprigSurveyResult *)result {
22
+ SurveyState state = [result surveyState];
23
+ NSString *surveyStateString = [self getSurveyState:state];
24
+ NSInteger surveyId = [result surveyId];
25
+ return @{@"surveyState": surveyStateString, @"surveyId": @(surveyId)};
26
+ }
27
+
21
28
  - (NSString *)getSurveyState:(SurveyState)surveyState
22
29
  {
23
30
  NSString *surveyStateBinding = @"NO_SURVEY";
@@ -31,6 +38,9 @@
31
38
  case SurveyStateNoSurvey:
32
39
  surveyStateBinding = @"NO_SURVEY";
33
40
  break;
41
+ case SurveyStatePreviousSurveyReady:
42
+ surveyStateBinding = @"PREVIOUS_SURVEY_READY";
43
+ break;
34
44
  }
35
45
  return surveyStateBinding;
36
46
  }
@@ -58,6 +68,7 @@ RCT_EXPORT_METHOD(configure:(NSString *)environmentId configuration:(NSDictionar
58
68
  [[UserLeap shared] _passWithRnExtractor:self];
59
69
  }
60
70
 
71
+
61
72
  RCT_EXPORT_METHOD(setPreviewKey:(NSString *)previewKey)
62
73
  {
63
74
  [[UserLeap shared] setPreviewKey:previewKey];
@@ -68,6 +79,7 @@ RCT_EXPORT_METHOD(presentSurvey)
68
79
  [[UserLeap shared] presentSurveyFrom:RCTPresentedViewController()];
69
80
  }
70
81
 
82
+ /// Use trackEvent instead of track
71
83
  RCT_EXPORT_METHOD(track:(NSString *)eventName
72
84
  surveyStateCallback:(RCTResponseSenderBlock)callback)
73
85
  {
@@ -80,33 +92,50 @@ RCT_EXPORT_METHOD(track:(NSString *)eventName
80
92
  }
81
93
  }
82
94
 
95
+ RCT_EXPORT_METHOD(trackEvent:(NSString *)eventName
96
+ surveyResultCallback:(RCTResponseSenderBlock)callback)
97
+ {
98
+ EventPayload *payload = [[EventPayload alloc] initWithEventName:eventName userId:nil partnerAnonymousId:nil properties:nil handler:nil resultHandler:^(SprigSurveyResult * result) {
99
+ if (callback != nil) {
100
+ callback(@[[self parseSurveyResult:result]]);
101
+ }
102
+ }];
103
+ [[UserLeap shared] trackWithPayload: payload];
104
+ }
105
+
106
+ /// Use trackEventWithProperties instead of trackWithProperties
83
107
  RCT_EXPORT_METHOD(trackWithProperties:(NSString *)eventName
84
108
  userId:(NSString * _Nullable)userId
85
109
  partnerAnonymousId:(NSString * _Nullable)partnerAnonymousId
86
110
  properties: (NSDictionary *)properties
87
111
  surveyStateCallback:(RCTResponseSenderBlock)callback)
88
112
  {
89
- if (callback != nil) {
90
113
  [[UserLeap shared] trackWithEventName:eventName userId:userId partnerAnonymousId:partnerAnonymousId properties:properties handler:^(enum SurveyState surveyState) {
91
- NSString *surveyStateBinding = @"NO_SURVEY";
92
- switch(surveyState) {
93
- case SurveyStateDisabled:
94
- surveyStateBinding = @"DISABLED";
95
- break;
96
- case SurveyStateReady:
97
- surveyStateBinding = @"READY";
98
- break;
99
- case SurveyStateNoSurvey:
100
- surveyStateBinding = @"NO_SURVEY";
101
- break;
114
+ callback(@[[self getSurveyState:surveyState]]);
115
+ }];
116
+
117
+ }
118
+
119
+ RCT_EXPORT_METHOD(trackEventWithProperties:(NSString *)eventName
120
+ userId:(NSString * _Nullable)userId
121
+ partnerAnonymousId:(NSString * _Nullable)partnerAnonymousId
122
+ properties: (NSDictionary *)properties
123
+ surveyResultCallback:(RCTResponseSenderBlock)callback)
124
+ {
125
+ EventPayload *payload = [[EventPayload alloc] initWithEventName:eventName
126
+ userId:userId
127
+ partnerAnonymousId:partnerAnonymousId
128
+ properties:properties
129
+ handler:nil
130
+ resultHandler:^(SprigSurveyResult * result) {
131
+ if (callback != nil) {
132
+ callback(@[[self parseSurveyResult:result]]);
102
133
  }
103
- callback(@[surveyStateBinding]);
104
134
  }];
105
- } else {
106
- [[UserLeap shared] trackWithEventName:eventName userId:userId partnerAnonymousId:partnerAnonymousId properties:properties handler:nil];
107
- }
135
+ [[UserLeap shared] trackWithPayload: payload];
108
136
  }
109
137
 
138
+ /// Use trackEventAndIdentify instead of trackAndIdentify
110
139
  RCT_EXPORT_METHOD(trackAndIdentify:(NSString *)eventName
111
140
  userId:(NSString *)userId
112
141
  partnerAnonymousId:(NSString *)partnerAnonymousId
@@ -119,7 +148,24 @@ RCT_EXPORT_METHOD(trackAndIdentify:(NSString *)eventName
119
148
  } else {
120
149
  [[UserLeap shared] trackWithEventName:eventName userId:userId partnerAnonymousId:partnerAnonymousId handler:nil];
121
150
  }
122
-
151
+ }
152
+
153
+ RCT_EXPORT_METHOD(trackEventAndIdentify:(NSString *)eventName
154
+ userId:(NSString *)userId
155
+ partnerAnonymousId:(NSString *)partnerAnonymousId
156
+ surveyResultCallback:(RCTResponseSenderBlock)callback)
157
+ {
158
+ EventPayload *payload = [[EventPayload alloc] initWithEventName:eventName
159
+ userId:userId
160
+ partnerAnonymousId:partnerAnonymousId
161
+ properties:nil
162
+ handler:nil
163
+ resultHandler:^(SprigSurveyResult * result) {
164
+ if (callback != nil) {
165
+ callback(@[[self parseSurveyResult:result]]);
166
+ }
167
+ }];
168
+ [[UserLeap shared] trackWithPayload: payload];
123
169
  }
124
170
 
125
171
  RCT_EXPORT_METHOD(setEmailAddress:(NSString *)emailAddress)
@@ -168,21 +214,54 @@ RCT_EXPORT_METHOD(trackIdentifyAndPresent:(NSString *)eventName userId:(NSString
168
214
  }
169
215
 
170
216
  - (_SGRNTextProperties *)textPropertiesFromView:(UIView * _Nonnull)passedView {
171
- if (![passedView isKindOfClass:[RCTTextView class]]) { return nil;}
172
- RCTUIManager* uiManager = [self.bridge moduleForClass:[RCTUIManager class]];
173
- RCTTextView *textView = (RCTTextView *)passedView;
174
- __block _SGRNTextProperties *textProperties = [[_SGRNTextProperties alloc] init];
175
- dispatch_sync(RCTGetUIManagerQueue(), ^{
176
- RCTShadowView *shadowView = [uiManager shadowViewForReactTag:textView.reactTag];
177
- if ([shadowView isKindOfClass:[RCTTextShadowView class]]) {
178
- RCTTextShadowView *textShadowView = (RCTTextShadowView *)shadowView;
179
- textProperties.text = [self extractText: textShadowView.reactSubviews];
180
- textProperties.color = textShadowView.textAttributes.foregroundColor;
181
- textProperties.alignment = textShadowView.textAttributes.alignment;
182
- textProperties.font = textShadowView.textAttributes.effectiveFont;
217
+
218
+ // Legacy for pre new arch.
219
+ if ([passedView isKindOfClass:[RCTTextView class]]) {
220
+ RCTUIManager* uiManager = [self.bridge moduleForClass:[RCTUIManager class]];
221
+ RCTTextView *textView = (RCTTextView *)passedView;
222
+ __block _SGRNTextProperties *textProperties = [[_SGRNTextProperties alloc] init];
223
+ dispatch_sync(RCTGetUIManagerQueue(), ^{
224
+ RCTShadowView *shadowView = [uiManager shadowViewForReactTag:textView.reactTag];
225
+ if ([shadowView isKindOfClass:[RCTTextShadowView class]]) {
226
+ RCTTextShadowView *textShadowView = (RCTTextShadowView *)shadowView;
227
+ NSString *text = [self extractText: textShadowView.reactSubviews];
228
+ if (text.length == 0) return;
229
+ textProperties.text = text;
230
+ textProperties.color = textShadowView.textAttributes.foregroundColor;
231
+ textProperties.alignment = textShadowView.textAttributes.alignment;
232
+ textProperties.font = textShadowView.textAttributes.effectiveFont;
233
+ }
234
+ });
235
+ if (textProperties.text.length == 0) return nil;
236
+ return textProperties;
237
+ }
238
+
239
+ // Text under new arch.
240
+ Class ParagraphComponentView = NSClassFromString(@"RCTParagraphComponentView");
241
+ if (ParagraphComponentView && [passedView isKindOfClass:ParagraphComponentView]) {
242
+ NSString *text = [self extractTextFromParagraphComponentView:passedView];
243
+ if (text.length == 0) return nil;
244
+ _SGRNTextProperties *textProperties = [[_SGRNTextProperties alloc] init];
245
+ textProperties.text = text;
246
+ if ([passedView respondsToSelector:@selector(attributedText)]) {
247
+ NSAttributedString *attrText = [passedView performSelector:@selector(attributedText)];
248
+ if (attrText.length > 0) {
249
+ NSDictionary *attrs = [attrText attributesAtIndex:0 effectiveRange:nil];
250
+ if (!textProperties.color && attrs[NSForegroundColorAttributeName]) {
251
+ textProperties.color = attrs[NSForegroundColorAttributeName];
252
+ }
253
+ if (!textProperties.font && attrs[NSFontAttributeName]) {
254
+ textProperties.font = attrs[NSFontAttributeName];
255
+ }
256
+ NSParagraphStyle *style = attrs[NSParagraphStyleAttributeName];
257
+ if (style) {
258
+ textProperties.alignment = style.alignment;
259
+ }
260
+ }
183
261
  }
184
- });
185
- return textProperties;
262
+ return textProperties;
263
+ }
264
+ return nil;
186
265
  }
187
266
 
188
267
  RCT_EXPORT_METHOD(dismissActiveSurvey)
@@ -225,4 +304,22 @@ RCT_EXPORT_METHOD(unpauseDisplayingSurveys)
225
304
  }
226
305
  return text;
227
306
  }
307
+
308
+ - (NSString *)extractTextFromParagraphComponentView:(UIView *)view {
309
+ if ([view respondsToSelector:@selector(attributedText)]) {
310
+ NSAttributedString *attrText = [view performSelector:@selector(attributedText)];
311
+ return attrText.string;
312
+ }
313
+ if ([view respondsToSelector:@selector(text)]) {
314
+ NSString *text = [view performSelector:@selector(text)];
315
+ return text;
316
+ }
317
+ NSMutableString *result = [NSMutableString string];
318
+ for (UIView *subview in view.subviews) {
319
+ NSString *subText = [self extractTextFromParagraphComponentView:subview];
320
+ if (subText) [result appendString:subText];
321
+ }
322
+ return result;
323
+ }
324
+
228
325
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-userleap",
3
- "version": "2.20.0",
3
+ "version": "3.0.0",
4
4
  "description": "React Native module for UserLeap",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -17,11 +17,6 @@
17
17
  "scripts": {
18
18
  "test": "echo \"Error: no test specified\" && exit 1"
19
19
  },
20
- "repository": {
21
- "type": "git",
22
- "url": "git+https://github.com/UserLeap/react-native-userleap.git",
23
- "baseUrl": "https://github.com/UserLeap/react-native-userleap"
24
- },
25
20
  "keywords": [
26
21
  "userleap",
27
22
  "react-native",
@@ -38,9 +33,12 @@
38
33
  "babel-jest": "29.7.0",
39
34
  "eslint": "7.32.0",
40
35
  "jest": "29.7.0",
41
- "metro-react-native-babel-preset": "0.67.0",
42
- "react": "18.2.0",
43
- "react-native": "0.74.7",
36
+ "@react-native/babel-preset": "^0.81.0",
37
+ "react": "19.1.0",
38
+ "react-native": "0.81.0",
44
39
  "react-test-renderer": "18.2.0"
40
+ },
41
+ "overrides": {
42
+ "js-yaml": "3.14.2"
45
43
  }
46
- }
44
+ }
@@ -19,5 +19,5 @@ Pod::Spec.new do |s|
19
19
  s.requires_arc = true
20
20
 
21
21
  s.dependency "React"
22
- s.dependency "UserLeapKit", "4.27.3"
22
+ s.dependency "UserLeapKit", "4.29.0"
23
23
  end