@amplytools/react-native-amply-sdk 0.14.0 → 0.15.1

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 (78) hide show
  1. package/AmplyReactNative.podspec +1 -1
  2. package/README.md +41 -0
  3. package/android/build.gradle +1 -1
  4. package/android/src/main/java/tools/amply/sdk/reactnative/AmplyModule.kt +22 -0
  5. package/android/src/main/java/tools/amply/sdk/reactnative/core/AmplyClient.kt +11 -0
  6. package/android/src/main/java/tools/amply/sdk/reactnative/core/DefaultAmplyClient.kt +29 -0
  7. package/android/src/main/java/tools/amply/sdk/reactnative/model/CampaignTouchPayload.kt +12 -0
  8. package/android/src/newarch/java/tools/amply/sdk/reactnative/NativeAmplyModuleSpec.java +8 -0
  9. package/android/src/newarch/jni/AmplyReactNative-generated.cpp +12 -0
  10. package/dist/cjs/campaignTouch.js +44 -0
  11. package/dist/cjs/campaignTouch.js.map +1 -0
  12. package/dist/cjs/hooks/useAmplySystemEvents.js +60 -0
  13. package/dist/cjs/hooks/useAmplySystemEvents.js.map +1 -0
  14. package/dist/cjs/index.js +723 -0
  15. package/dist/cjs/index.js.map +1 -0
  16. package/dist/cjs/nativeModule.js +55 -0
  17. package/dist/cjs/nativeModule.js.map +1 -0
  18. package/dist/cjs/nativeSpecs/NativeAmplyModule.js +5 -0
  19. package/dist/cjs/nativeSpecs/NativeAmplyModule.js.map +1 -0
  20. package/dist/cjs/systemEventUtils.js +47 -0
  21. package/dist/cjs/systemEventUtils.js.map +1 -0
  22. package/dist/cjs/systemEvents.js +27 -0
  23. package/dist/cjs/systemEvents.js.map +1 -0
  24. package/dist/src/campaignTouch.d.ts +36 -0
  25. package/dist/src/campaignTouch.d.ts.map +1 -0
  26. package/dist/src/campaignTouch.js +39 -0
  27. package/dist/src/index.d.ts +35 -0
  28. package/dist/src/index.d.ts.map +1 -1
  29. package/dist/src/index.js +120 -0
  30. package/dist/src/nativeSpecs/NativeAmplyModule.d.ts +28 -0
  31. package/dist/src/nativeSpecs/NativeAmplyModule.d.ts.map +1 -1
  32. package/dist/src/systemEventUtils.d.ts.map +1 -1
  33. package/dist/src/systemEventUtils.js +3 -1
  34. package/docs/ARCHITECTURE.md +28 -19
  35. package/docs/DEVELOPMENT.md +2 -1
  36. package/ios/Sources/AmplyReactNative/AmplyModule.mm +56 -0
  37. package/ios/Sources/AmplyReactNative/AmplyReactNative/AmplyReactNative-generated.mm +14 -0
  38. package/ios/Sources/AmplyReactNative/AmplyReactNative/AmplyReactNative.h +4 -0
  39. package/package.json +12 -6
  40. package/scripts/check-package.js +159 -0
  41. package/src/campaignTouch.ts +74 -0
  42. package/src/index.ts +124 -0
  43. package/src/nativeSpecs/NativeAmplyModule.ts +29 -0
  44. package/src/systemEventUtils.ts +3 -1
  45. package/dist/index.js +0 -272
  46. package/dist/index.js.map +0 -1
  47. package/dist/index.mjs +0 -234
  48. package/dist/index.mjs.map +0 -1
  49. package/dist/plugin/index.d.ts +0 -6
  50. package/dist/plugin/index.d.ts.map +0 -1
  51. package/dist/plugin/index.js +0 -186
  52. package/dist/plugin/index.js.map +0 -1
  53. package/dist/plugin/index.mjs +0 -169
  54. package/dist/plugin/index.mjs.map +0 -1
  55. package/dist/plugin/withAmply.d.ts +0 -12
  56. package/dist/plugin/withAmply.d.ts.map +0 -1
  57. package/dist/src/__tests__/appLanguage.test.d.ts +0 -43
  58. package/dist/src/__tests__/appLanguage.test.d.ts.map +0 -1
  59. package/dist/src/__tests__/appLanguage.test.js +0 -211
  60. package/dist/src/__tests__/campaignResolved.test.d.ts +0 -2
  61. package/dist/src/__tests__/campaignResolved.test.d.ts.map +0 -1
  62. package/dist/src/__tests__/campaignResolved.test.js +0 -208
  63. package/dist/src/__tests__/gateRegistrationLifecycle.test.d.ts +0 -2
  64. package/dist/src/__tests__/gateRegistrationLifecycle.test.d.ts.map +0 -1
  65. package/dist/src/__tests__/gateRegistrationLifecycle.test.js +0 -215
  66. package/dist/src/__tests__/index.test.d.ts +0 -2
  67. package/dist/src/__tests__/index.test.d.ts.map +0 -1
  68. package/dist/src/__tests__/index.test.js +0 -204
  69. package/dist/src/__tests__/systemEventUtils.test.d.ts +0 -2
  70. package/dist/src/__tests__/systemEventUtils.test.d.ts.map +0 -1
  71. package/dist/src/__tests__/systemEventUtils.test.js +0 -56
  72. package/dist/tsconfig.tsbuildinfo +0 -1
  73. package/plugin/tsconfig.tsbuildinfo +0 -1
  74. package/src/__tests__/appLanguage.test.ts +0 -296
  75. package/src/__tests__/campaignResolved.test.ts +0 -294
  76. package/src/__tests__/gateRegistrationLifecycle.test.ts +0 -281
  77. package/src/__tests__/index.test.ts +0 -258
  78. package/src/__tests__/systemEventUtils.test.ts +0 -75
@@ -22,7 +22,7 @@ Pod::Spec.new do |s|
22
22
  s.dependency 'React-RCTFabric'
23
23
 
24
24
  # Amply KMP SDK from CocoaPods
25
- s.dependency 'AmplySDK', '~> 0.14.0'
25
+ s.dependency 'AmplySDK', '~> 0.15.0'
26
26
 
27
27
  s.libraries = 'c++'
28
28
  s.pod_target_xcconfig = {
package/README.md CHANGED
@@ -13,6 +13,7 @@ React Native TurboModule bridge for the Amply SDK. Enables event tracking, deepl
13
13
  | Debug/Logging | ✅ | Android, iOS |
14
14
  | Custom properties | ✅ | Android, iOS |
15
15
  | User identification | ✅ | Android, iOS |
16
+ | Last campaign touch (attribution in your analytics) | ✅ | Android, iOS |
16
17
 
17
18
  ## Requirements
18
19
 
@@ -75,6 +76,46 @@ const unsubscribe = await Amply.addDeepLinkListener((event) => {
75
76
  });
76
77
  ```
77
78
 
79
+ ### Debug launch
80
+
81
+ Amply can open a campaign on a real device from a link or QR code made in the dashboard, so you
82
+ can click through it inside your app. The link uses your app's URL scheme, so the app has to pass
83
+ incoming links to Amply:
84
+
85
+ ```typescript
86
+ import {Linking} from 'react-native';
87
+ import Amply from '@amplytools/react-native-amply-sdk';
88
+
89
+ const forward = (url: string | null) => {
90
+ if (url && Amply.handleUrl(url)) {
91
+ return; // an Amply link: Amply took it, do not route it
92
+ }
93
+ // ...your own link handling
94
+ };
95
+
96
+ Linking.getInitialURL().then(forward);
97
+ Linking.addEventListener('url', ({url}) => forward(url));
98
+ ```
99
+
100
+ `handleUrl` returns `true` when Amply recognised the link as its own, and `false` for any other
101
+ link (it never throws). It is fine to call it before `initialize` finishes, as on a cold start: an Amply link that arrives early is held (the newest one) and opened as soon as `initialize` succeeds; it is dropped if `initialize` fails. A
102
+ campaign launched this way is not counted in statistics.
103
+
104
+ With Expo Router, links are routed before your code sees them, so forward only from
105
+ `app/+native-intent.tsx` (and do not also forward with `Linking` above). An Amply link reaches the
106
+ SDK instead of the not-found screen, and every other link is routed as usual:
107
+
108
+ ```typescript
109
+ import Amply from '@amplytools/react-native-amply-sdk';
110
+
111
+ export function redirectSystemPath({path}: {path: string; initial: boolean}) {
112
+ if (Amply.handleUrl(path)) {
113
+ return '/'; // an Amply link: Amply took it
114
+ }
115
+ return path;
116
+ }
117
+ ```
118
+
78
119
  ### About the keys
79
120
 
80
121
  `apiKeySecret` ships inside your app: the SDK signs every request with it on the device. Keep it
@@ -15,7 +15,7 @@ buildscript {
15
15
  apply plugin: 'com.android.library'
16
16
  apply plugin: 'org.jetbrains.kotlin.android'
17
17
 
18
- def amplySdkVersion = project.findProperty('amplySdkVersion') ?: System.getenv('AMPLY_SDK_VERSION') ?: '0.14.0'
18
+ def amplySdkVersion = project.findProperty('amplySdkVersion') ?: System.getenv('AMPLY_SDK_VERSION') ?: '0.15.0'
19
19
  group = 'tools.amply'
20
20
  version = amplySdkVersion
21
21
 
@@ -5,6 +5,7 @@ import tools.amply.sdk.reactnative.core.AmplyClient
5
5
  import tools.amply.sdk.reactnative.core.DefaultAmplyClient
6
6
  import tools.amply.sdk.reactnative.core.GateDecision
7
7
  import tools.amply.sdk.reactnative.model.AmplyInitializationOptions
8
+ import tools.amply.sdk.reactnative.model.CampaignTouchPayload
8
9
  import tools.amply.sdk.reactnative.model.DataSetType
9
10
  import tools.amply.sdk.reactnative.model.DataSetType.EventParam
10
11
  import tools.amply.sdk.reactnative.model.DataSetType.Events
@@ -153,6 +154,16 @@ class AmplyModule(reactContext: ReactApplicationContext) :
153
154
  }
154
155
  }
155
156
 
157
+ override fun getLastCampaignTouch(withinMs: Double, promise: Promise) {
158
+ scope.launch {
159
+ try {
160
+ promise.resolve(client.getLastCampaignTouch(withinMs.toLong())?.toWritableMap())
161
+ } catch (throwable: Throwable) {
162
+ promise.reject(CAMPAIGN_TOUCH_ERROR, throwable)
163
+ }
164
+ }
165
+ }
166
+
156
167
  override fun getDataSetSnapshot(type: ReadableMap, promise: Promise) {
157
168
  val datasetType = try {
158
169
  type.toDataSetType()
@@ -199,6 +210,8 @@ class AmplyModule(reactContext: ReactApplicationContext) :
199
210
  client.setAppLanguage(tag)
200
211
  }
201
212
 
213
+ override fun handleUrl(url: String): Boolean = client.handleUrl(url)
214
+
202
215
  override fun setLogLevel(level: String) {
203
216
  val newLevel = LogLevel.fromString(level)
204
217
  client.setLogLevel(newLevel)
@@ -427,6 +440,14 @@ class AmplyModule(reactContext: ReactApplicationContext) :
427
440
  putMap("properties", properties.toWritableMap())
428
441
  }
429
442
 
443
+ private fun CampaignTouchPayload.toWritableMap(): WritableMap =
444
+ Arguments.createMap().apply {
445
+ putString("campaignId", campaignId)
446
+ putString("campaignName", campaignName)
447
+ putString("kind", kind)
448
+ putDouble("atMs", atMs.toDouble())
449
+ }
450
+
430
451
  private fun List<EventEnvelope>.toWritableArray(): WritableArray {
431
452
  val array = Arguments.createArray()
432
453
  forEach { event ->
@@ -530,6 +551,7 @@ class AmplyModule(reactContext: ReactApplicationContext) :
530
551
  private const val ARGUMENT_ERROR = "AMP_INVALID_ARGUMENT"
531
552
  private const val TRACK_ERROR = "AMP_TRACK_FAILED"
532
553
  private const val EVENTS_ERROR = "AMP_EVENTS_FAILED"
554
+ private const val CAMPAIGN_TOUCH_ERROR = "AMP_CAMPAIGN_TOUCH_FAILED"
533
555
  private const val DATASET_ERROR = "AMP_DATASET_FAILED"
534
556
  private const val CUSTOM_PROP_ERROR = "AMP_CUSTOM_PROP_FAILED"
535
557
  }
@@ -2,6 +2,7 @@ package tools.amply.sdk.reactnative.core
2
2
 
3
3
  import android.app.Activity
4
4
  import tools.amply.sdk.reactnative.model.AmplyInitializationOptions
5
+ import tools.amply.sdk.reactnative.model.CampaignTouchPayload
5
6
  import tools.amply.sdk.reactnative.model.DataSetType
6
7
  import tools.amply.sdk.reactnative.model.DeepLinkPayload
7
8
  import tools.amply.sdk.reactnative.model.EventEnvelope
@@ -65,6 +66,13 @@ interface AmplyClient {
65
66
 
66
67
  suspend fun getRecentEvents(limit: Int): List<EventEnvelope>
67
68
 
69
+ /**
70
+ * The SDK's `getLastCampaignTouch(withinMs)`: the newest campaign touch at most [withinMs] ago
71
+ * (clamped by the SDK to [0, 30 days]). Null when there is none, and also before
72
+ * initialisation — the SDK answers null there rather than throwing, and so does the bridge.
73
+ */
74
+ suspend fun getLastCampaignTouch(withinMs: Long): CampaignTouchPayload?
75
+
68
76
  suspend fun getDataSetSnapshot(type: DataSetType): Map<String, Any?>
69
77
 
70
78
  fun registerDeepLinkListener()
@@ -74,6 +82,9 @@ interface AmplyClient {
74
82
 
75
83
  fun setAppLanguage(tag: String?)
76
84
 
85
+ /** True when the SDK recognised [url] as its own and took it over; false otherwise or when not initialised. */
86
+ fun handleUrl(url: String): Boolean
87
+
77
88
  fun setLogLevel(level: LogLevel)
78
89
  fun getLogLevel(): LogLevel
79
90
 
@@ -5,6 +5,7 @@ import android.app.Application
5
5
  import android.os.Handler
6
6
  import android.os.Looper
7
7
  import tools.amply.sdk.reactnative.model.AmplyInitializationOptions
8
+ import tools.amply.sdk.reactnative.model.CampaignTouchPayload
8
9
  import tools.amply.sdk.reactnative.model.DataSetType
9
10
  import tools.amply.sdk.reactnative.model.DeepLinkPayload
10
11
  import tools.amply.sdk.reactnative.model.EventEnvelope
@@ -30,6 +31,8 @@ import tools.amply.sdk.actions.CampaignPresenter
30
31
  import tools.amply.sdk.actions.CampaignResolution
31
32
  import tools.amply.sdk.actions.CampaignResult
32
33
  import tools.amply.sdk.actions.DeepLinkListener
34
+ import tools.amply.sdk.campaigns.CampaignTouch
35
+ import tools.amply.sdk.campaigns.CampaignTouchKind
33
36
  import tools.amply.sdk.core.GateDecision as KmpGateDecision
34
37
  import tools.amply.sdk.core.ProceedReason as KmpProceedReason
35
38
  import tools.amply.sdk.config.AmplyConfig
@@ -291,6 +294,26 @@ class DefaultAmplyClient(
291
294
  }
292
295
  }
293
296
 
297
+ override suspend fun getLastCampaignTouch(withinMs: Long): CampaignTouchPayload? {
298
+ // Not requireInstance(): "not initialised" is an answer here (null), as it is in the SDK.
299
+ val instance = amplyInstance ?: return null
300
+ return withContext(Dispatchers.IO) {
301
+ instance.getLastCampaignTouch(withinMs)?.toPayload()
302
+ }
303
+ }
304
+
305
+ private fun CampaignTouch.toPayload(): CampaignTouchPayload = CampaignTouchPayload(
306
+ campaignId = campaignId,
307
+ campaignName = campaignName,
308
+ // Exhaustive on purpose: a kind added to the SDK must fail this build, not reach JS as a
309
+ // value the TS type does not admit.
310
+ kind = when (kind) {
311
+ CampaignTouchKind.SHOWN -> "shown"
312
+ CampaignTouchKind.ENGAGED -> "engaged"
313
+ },
314
+ atMs = atMs,
315
+ )
316
+
294
317
  override suspend fun getDataSetSnapshot(type: DataSetType): Map<String, Any?> {
295
318
  val instance = requireInstance()
296
319
  val nativeType = type.toNativeDataSetType()
@@ -400,6 +423,12 @@ class DefaultAmplyClient(
400
423
  }
401
424
  }
402
425
 
426
+ override fun handleUrl(url: String): Boolean {
427
+ // Not buffered: a link is only meaningful to a live SDK, and the answer is needed right now.
428
+ val instance = amplyInstance ?: return false
429
+ return instance.handleUrl(url)
430
+ }
431
+
403
432
  override fun setCustomProperty(key: String, value: Any) {
404
433
  synchronized(propertyLock) {
405
434
  val instance = amplyInstance
@@ -0,0 +1,12 @@
1
+ package tools.amply.sdk.reactnative.model
2
+
3
+ /**
4
+ * The bridge's copy of the SDK's `CampaignTouch`, with [kind] already in its JS spelling
5
+ * (`"shown"` / `"engaged"`).
6
+ */
7
+ data class CampaignTouchPayload(
8
+ val campaignId: String,
9
+ val campaignName: String,
10
+ val kind: String,
11
+ val atMs: Long,
12
+ )
@@ -78,6 +78,10 @@ public abstract class NativeAmplyModuleSpec extends ReactContextBaseJavaModule i
78
78
  @DoNotStrip
79
79
  public abstract void getRecentEvents(double limit, Promise promise);
80
80
 
81
+ @ReactMethod
82
+ @DoNotStrip
83
+ public abstract void getLastCampaignTouch(double withinMs, Promise promise);
84
+
81
85
  @ReactMethod
82
86
  @DoNotStrip
83
87
  public abstract void getDataSetSnapshot(ReadableMap type, Promise promise);
@@ -94,6 +98,10 @@ public abstract class NativeAmplyModuleSpec extends ReactContextBaseJavaModule i
94
98
  @DoNotStrip
95
99
  public abstract void setAppLanguage(@Nullable String tag);
96
100
 
101
+ @ReactMethod(isBlockingSynchronousMethod = true)
102
+ @DoNotStrip
103
+ public abstract boolean handleUrl(String url);
104
+
97
105
  @ReactMethod
98
106
  @DoNotStrip
99
107
  public abstract void setLogLevel(String level);
@@ -52,6 +52,11 @@ static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_getRecentEve
52
52
  return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "getRecentEvents", "(DLcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
53
53
  }
54
54
 
55
+ static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_getLastCampaignTouch(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
56
+ static jmethodID cachedMethodId = nullptr;
57
+ return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "getLastCampaignTouch", "(DLcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
58
+ }
59
+
55
60
  static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_getDataSetSnapshot(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
56
61
  static jmethodID cachedMethodId = nullptr;
57
62
  return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "getDataSetSnapshot", "(Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
@@ -72,6 +77,11 @@ static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_setAppLangua
72
77
  return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, VoidKind, "setAppLanguage", "(Ljava/lang/String;)V", args, count, cachedMethodId);
73
78
  }
74
79
 
80
+ static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_handleUrl(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
81
+ static jmethodID cachedMethodId = nullptr;
82
+ return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, BooleanKind, "handleUrl", "(Ljava/lang/String;)Z", args, count, cachedMethodId);
83
+ }
84
+
75
85
  static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_setLogLevel(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
76
86
  static jmethodID cachedMethodId = nullptr;
77
87
  return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, VoidKind, "setLogLevel", "(Ljava/lang/String;)V", args, count, cachedMethodId);
@@ -122,10 +132,12 @@ NativeAmplyModuleSpecJSI::NativeAmplyModuleSpecJSI(const JavaTurboModule::InitPa
122
132
  methodMap_["registerGate"] = MethodMetadata {3, __hostFunction_NativeAmplyModuleSpecJSI_registerGate};
123
133
  methodMap_["unregisterGate"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_unregisterGate};
124
134
  methodMap_["getRecentEvents"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_getRecentEvents};
135
+ methodMap_["getLastCampaignTouch"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_getLastCampaignTouch};
125
136
  methodMap_["getDataSetSnapshot"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_getDataSetSnapshot};
126
137
  methodMap_["registerDeepLinkListener"] = MethodMetadata {0, __hostFunction_NativeAmplyModuleSpecJSI_registerDeepLinkListener};
127
138
  methodMap_["setUserId"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_setUserId};
128
139
  methodMap_["setAppLanguage"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_setAppLanguage};
140
+ methodMap_["handleUrl"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_handleUrl};
129
141
  methodMap_["setLogLevel"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_setLogLevel};
130
142
  methodMap_["getLogLevel"] = MethodMetadata {0, __hostFunction_NativeAmplyModuleSpecJSI_getLogLevel};
131
143
  methodMap_["setCustomProperties"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_setCustomProperties};
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertValidWithinMs = assertValidWithinMs;
4
+ exports.toCampaignTouch = toCampaignTouch;
5
+ /** Thrown (as a rejection) by `getLastCampaignTouch` for a window that is not a usable number. */
6
+ function assertValidWithinMs(withinMs) {
7
+ if (typeof withinMs !== 'number' || !Number.isFinite(withinMs) || withinMs < 0) {
8
+ throw new RangeError(`[Amply] getLastCampaignTouch: withinMs must be a finite number >= 0, got ${String(withinMs)}`);
9
+ }
10
+ }
11
+ function toKind(raw) {
12
+ if (typeof raw !== 'string') {
13
+ return null;
14
+ }
15
+ const lowered = raw.toLowerCase();
16
+ return lowered === 'shown' || lowered === 'engaged' ? lowered : null;
17
+ }
18
+ /**
19
+ * Narrows what the native bridge resolved to the public type. Anything it cannot read as a
20
+ * touch becomes null with a warning: the caller asked "was there a campaign", and a payload this
21
+ * bridge does not understand is not one it can vouch for.
22
+ */
23
+ function toCampaignTouch(payload) {
24
+ if (payload == null) {
25
+ return null;
26
+ }
27
+ const kind = toKind(payload.kind);
28
+ const isReadable = kind !== null &&
29
+ typeof payload.campaignId === 'string' &&
30
+ payload.campaignId.length > 0 &&
31
+ typeof payload.atMs === 'number' &&
32
+ Number.isFinite(payload.atMs);
33
+ if (!isReadable) {
34
+ console.warn('[Amply] getLastCampaignTouch: ignoring an unreadable native payload', payload);
35
+ return null;
36
+ }
37
+ return {
38
+ campaignId: payload.campaignId,
39
+ campaignName: typeof payload.campaignName === 'string' ? payload.campaignName : '',
40
+ kind,
41
+ atMs: payload.atMs,
42
+ };
43
+ }
44
+ //# sourceMappingURL=campaignTouch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"campaignTouch.js","sourceRoot":"","sources":["../../src/campaignTouch.ts"],"names":[],"mappings":";;AA+BA,kDAMC;AAeD,0CAqBC;AA3CD,kGAAkG;AAClG,SAAgB,mBAAmB,CAAC,QAAiB;IACnD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QAC/E,MAAM,IAAI,UAAU,CAClB,4EAA4E,MAAM,CAAC,QAAQ,CAAC,EAAE,CAC/F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,GAAY;IAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAClC,OAAO,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,OAAgD;IAC9E,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,UAAU,GACd,IAAI,KAAK,IAAI;QACb,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ;QACtC,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QAC7B,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;QAChC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,qEAAqE,EAAE,OAAO,CAAC,CAAC;QAC7F,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,YAAY,EAAE,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;QAClF,IAAI;QACJ,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useAmplySystemEvents = useAmplySystemEvents;
4
+ const react_1 = require("react");
5
+ const systemEvents_1 = require("../systemEvents");
6
+ function useAmplySystemEvents(options = {}) {
7
+ const { maxEntries = 50, dedupe = true, onEvent } = options;
8
+ const [events, setEvents] = (0, react_1.useState)([]);
9
+ const seenKeysRef = (0, react_1.useRef)(new Set());
10
+ const handlerRef = (0, react_1.useRef)(onEvent);
11
+ (0, react_1.useEffect)(() => {
12
+ handlerRef.current = onEvent;
13
+ }, [onEvent]);
14
+ (0, react_1.useEffect)(() => {
15
+ let unsubscribe;
16
+ let isMounted = true;
17
+ (0, systemEvents_1.addSystemEventListener)(event => {
18
+ var _a;
19
+ if (!isMounted) {
20
+ return;
21
+ }
22
+ if (dedupe) {
23
+ const key = `${event.name}-${event.timestamp}`;
24
+ if (seenKeysRef.current.has(key)) {
25
+ return;
26
+ }
27
+ seenKeysRef.current.add(key);
28
+ }
29
+ (_a = handlerRef.current) === null || _a === void 0 ? void 0 : _a.call(handlerRef, event);
30
+ setEvents(prev => {
31
+ const next = [...prev, event];
32
+ if (next.length > maxEntries) {
33
+ next.splice(0, next.length - maxEntries);
34
+ }
35
+ return next;
36
+ });
37
+ })
38
+ .then(unsub => {
39
+ if (!isMounted) {
40
+ unsub();
41
+ }
42
+ else {
43
+ unsubscribe = unsub;
44
+ }
45
+ })
46
+ .catch(error => {
47
+ console.warn('[Amply] system event listener error', error);
48
+ });
49
+ return () => {
50
+ isMounted = false;
51
+ unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
52
+ };
53
+ }, [dedupe, maxEntries]);
54
+ const reset = (0, react_1.useCallback)(() => {
55
+ seenKeysRef.current.clear();
56
+ setEvents([]);
57
+ }, []);
58
+ return { events, reset };
59
+ }
60
+ //# sourceMappingURL=useAmplySystemEvents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAmplySystemEvents.js","sourceRoot":"","sources":["../../../src/hooks/useAmplySystemEvents.ts"],"names":[],"mappings":";;AAeA,oDA2DC;AA1ED,iCAA+D;AAE/D,kDAAuD;AAavD,SAAgB,oBAAoB,CAClC,UAAuC,EAAE;IAEzC,MAAM,EAAC,UAAU,GAAG,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAC,GAAG,OAAO,CAAC;IAC1D,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAA,gBAAQ,EAAgB,EAAE,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,IAAA,cAAM,EAAc,IAAI,GAAG,EAAE,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,IAAA,cAAM,EAAiB,OAAO,CAAC,CAAC;IAEnD,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAC/B,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,WAAqC,CAAC;QAC1C,IAAI,SAAS,GAAG,IAAI,CAAC;QAErB,IAAA,qCAAsB,EAAC,KAAK,CAAC,EAAE;;YAC7B,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO;YACT,CAAC;YACD,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC/C,IAAI,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBACjC,OAAO;gBACT,CAAC;gBACD,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC;YACD,MAAA,UAAU,CAAC,OAAO,2DAAG,KAAK,CAAC,CAAC;YAC5B,SAAS,CAAC,IAAI,CAAC,EAAE;gBACf,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;gBAC9B,IAAI,IAAI,CAAC,MAAM,GAAG,UAAU,EAAE,CAAC;oBAC7B,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;gBAC3C,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;aACC,IAAI,CAAC,KAAK,CAAC,EAAE;YACZ,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,KAAK,EAAE,CAAC;YACV,CAAC;iBAAM,CAAC;gBACN,WAAW,GAAG,KAAK,CAAC;YACtB,CAAC;QACH,CAAC,CAAC;aACD,KAAK,CAAC,KAAK,CAAC,EAAE;YACb,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEL,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,KAAK,CAAC;YAClB,WAAW,aAAX,WAAW,uBAAX,WAAW,EAAI,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAEzB,MAAM,KAAK,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QAC7B,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC5B,SAAS,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAC,MAAM,EAAE,KAAK,EAAC,CAAC;AACzB,CAAC"}