diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index e2349bc3b..c578c57e3 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -1,26 +1,26 @@ PODS: - boost (1.84.0) - - DatadogCore (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogCrashReporting (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogCore (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogCrashReporting (3.10.0): + - DatadogInternal (= 3.10.0) - KSCrash/Filters (= 2.5.0) - KSCrash/Recording (= 2.5.0) - - DatadogFlags (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogInternal (3.8.2) - - DatadogLogs (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogRUM (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogFlags (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogInternal (3.10.0) + - DatadogLogs (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogRUM (3.10.0): + - DatadogInternal (= 3.10.0) - DatadogSDKReactNative (3.3.0): - - DatadogCore (= 3.8.2) - - DatadogCrashReporting (= 3.8.2) - - DatadogFlags (= 3.8.2) - - DatadogLogs (= 3.8.2) - - DatadogRUM (= 3.8.2) - - DatadogTrace (= 3.8.2) - - DatadogWebViewTracking (= 3.8.2) + - DatadogCore (= 3.10.0) + - DatadogCrashReporting (= 3.10.0) + - DatadogFlags (= 3.10.0) + - DatadogLogs (= 3.10.0) + - DatadogRUM (= 3.10.0) + - DatadogTrace (= 3.10.0) + - DatadogWebViewTracking (= 3.10.0) - DoubleConversion - glog - hermes-engine @@ -43,7 +43,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay (3.3.0): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.8.2) + - DatadogSessionReplay (= 3.10.0) - DoubleConversion - glog - hermes-engine @@ -65,9 +65,9 @@ PODS: - ReactCommon/turbomodule/core - Yoga - DatadogSDKReactNativeWebView (3.3.0): - - DatadogInternal (= 3.8.2) + - DatadogInternal (= 3.10.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.8.2) + - DatadogWebViewTracking (= 3.10.0) - DoubleConversion - glog - hermes-engine @@ -88,13 +88,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSessionReplay (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogTrace (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogSessionReplay (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogTrace (3.10.0): + - DatadogInternal (= 3.10.0) - OpenTelemetry-Swift-Api (~> 2.3.0) - - DatadogWebViewTracking (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogWebViewTracking (3.10.0): + - DatadogInternal (= 3.10.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.78.2) @@ -2088,18 +2088,18 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 - DatadogCore: 4375353a93311e99153ab3bc2fba10fefb295749 - DatadogCrashReporting: 4eedb27058432691a7e5f1bdcf3f72d8cc7c1a67 - DatadogFlags: fc732c5fa26259de55e83a13254522b38072d9d1 - DatadogInternal: a4640f67e249c9b4608e806cf1c8c10b17f66626 - DatadogLogs: 55e2c1c403e92f952f8ce451854c81f476b04d89 - DatadogRUM: 738b6b448df9af58ba0455451e628ac81040be04 - DatadogSDKReactNative: 7aad7c64a0dbc84955e0d93d35194b1f062fbe53 - DatadogSDKReactNativeSessionReplay: 19a01a7c1a6b33edcca8cd878b61e06e6d63a3fd - DatadogSDKReactNativeWebView: bba1f331858ce9cc34621337773b0f0516e78cef - DatadogSessionReplay: 4edcb9997acc8c378048932ffe0dd1e6bf2c18ca - DatadogTrace: faefc639bc2dd11a8ebbf9cbddf830b68eb62d1d - DatadogWebViewTracking: 50afac044e278ac0165c082b6b65dd8327fdefb4 + DatadogCore: be0f6c233bef27eda40acade29763943cb88935f + DatadogCrashReporting: 151f72523ee117000fed4b5e792de2349d38b331 + DatadogFlags: fbc1d17d2b83397c4f33817263a14b84a857ca1d + DatadogInternal: b81d08e4035de78140488b7422c239b17b89d251 + DatadogLogs: 20381f731050c500c85d36f4799cf0f28a56915e + DatadogRUM: 4f863c5330d85d6b54bb372d830929ad3b5006ab + DatadogSDKReactNative: 2d03d9524f43e93a40a466b53982bd303b045e1a + DatadogSDKReactNativeSessionReplay: 9225e86628c76a06d11c80200e06f10738018e59 + DatadogSDKReactNativeWebView: 25996d34b0b283c37d98e2e079e00b925d09edd1 + DatadogSessionReplay: aef317ccd62a089691899bc7153177deec3c66d6 + DatadogTrace: c51a2bde28197cb1bab50031c83a82e3a1dbcf30 + DatadogWebViewTracking: 7775faa20f2044f2948c2eb246889ba5ceefbd6a DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: e32d34492c519a2194ec9d7f5e7a79d11b73f91c @@ -2109,7 +2109,7 @@ SPEC CHECKSUMS: KSCrash: 80e1e24eaefbe5134934ae11ca8d7746586bc2ed OpenTelemetry-Swift-Api: 3d77582ab6837a63b65bf7d2eacc57d8f2595edd OpenTelemetry-Swift-Sdk: 69d60f0242e830366e359481edd575d6776eb983 - RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82 + RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809 RCTDeprecation: be794de7dc6ed8f9f7fbf525f86e7651b8b68746 RCTRequired: a83787b092ec554c2eb6019ff3f5b8d125472b3b RCTTypeSafety: 48ad3c858926b1c46f46a81a58822b476e178e2c diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index 2747021a3..ae411d9e2 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -1,26 +1,26 @@ PODS: - boost (1.84.0) - - DatadogCore (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogCrashReporting (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogCore (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogCrashReporting (3.10.0): + - DatadogInternal (= 3.10.0) - KSCrash/Filters (= 2.5.0) - KSCrash/Recording (= 2.5.0) - - DatadogFlags (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogInternal (3.8.2) - - DatadogLogs (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogRUM (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogFlags (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogInternal (3.10.0) + - DatadogLogs (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogRUM (3.10.0): + - DatadogInternal (= 3.10.0) - DatadogSDKReactNative (3.3.0): - - DatadogCore (= 3.8.2) - - DatadogCrashReporting (= 3.8.2) - - DatadogFlags (= 3.8.2) - - DatadogLogs (= 3.8.2) - - DatadogRUM (= 3.8.2) - - DatadogTrace (= 3.8.2) - - DatadogWebViewTracking (= 3.8.2) + - DatadogCore (= 3.10.0) + - DatadogCrashReporting (= 3.10.0) + - DatadogFlags (= 3.10.0) + - DatadogLogs (= 3.10.0) + - DatadogRUM (= 3.10.0) + - DatadogTrace (= 3.10.0) + - DatadogWebViewTracking (= 3.10.0) - DoubleConversion - glog - hermes-engine @@ -42,13 +42,13 @@ PODS: - ReactCommon/turbomodule/core - Yoga - DatadogSDKReactNative/Tests (3.3.0): - - DatadogCore (= 3.8.2) - - DatadogCrashReporting (= 3.8.2) - - DatadogFlags (= 3.8.2) - - DatadogLogs (= 3.8.2) - - DatadogRUM (= 3.8.2) - - DatadogTrace (= 3.8.2) - - DatadogWebViewTracking (= 3.8.2) + - DatadogCore (= 3.10.0) + - DatadogCrashReporting (= 3.10.0) + - DatadogFlags (= 3.10.0) + - DatadogLogs (= 3.10.0) + - DatadogRUM (= 3.10.0) + - DatadogTrace (= 3.10.0) + - DatadogWebViewTracking (= 3.10.0) - DoubleConversion - glog - hermes-engine @@ -69,11 +69,11 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogTrace (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogTrace (3.10.0): + - DatadogInternal (= 3.10.0) - OpenTelemetry-Swift-Api (~> 2.3.0) - - DatadogWebViewTracking (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogWebViewTracking (3.10.0): + - DatadogInternal (= 3.10.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1866,15 +1866,15 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: 4375353a93311e99153ab3bc2fba10fefb295749 - DatadogCrashReporting: 4eedb27058432691a7e5f1bdcf3f72d8cc7c1a67 - DatadogFlags: fc732c5fa26259de55e83a13254522b38072d9d1 - DatadogInternal: a4640f67e249c9b4608e806cf1c8c10b17f66626 - DatadogLogs: 55e2c1c403e92f952f8ce451854c81f476b04d89 - DatadogRUM: 738b6b448df9af58ba0455451e628ac81040be04 - DatadogSDKReactNative: cb133ceaf00328cac418f5398957eaf6a32e762d - DatadogTrace: faefc639bc2dd11a8ebbf9cbddf830b68eb62d1d - DatadogWebViewTracking: 50afac044e278ac0165c082b6b65dd8327fdefb4 + DatadogCore: be0f6c233bef27eda40acade29763943cb88935f + DatadogCrashReporting: 151f72523ee117000fed4b5e792de2349d38b331 + DatadogFlags: fbc1d17d2b83397c4f33817263a14b84a857ca1d + DatadogInternal: b81d08e4035de78140488b7422c239b17b89d251 + DatadogLogs: 20381f731050c500c85d36f4799cf0f28a56915e + DatadogRUM: 4f863c5330d85d6b54bb372d830929ad3b5006ab + DatadogSDKReactNative: ed20f3f410658475d8ef360d6723f880e9cbb1ee + DatadogTrace: c51a2bde28197cb1bab50031c83a82e3a1dbcf30 + DatadogWebViewTracking: 7775faa20f2044f2948c2eb246889ba5ceefbd6a DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 @@ -1883,65 +1883,65 @@ SPEC CHECKSUMS: hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11 KSCrash: 80e1e24eaefbe5134934ae11ca8d7746586bc2ed OpenTelemetry-Swift-Api: 3d77582ab6837a63b65bf7d2eacc57d8f2595edd - RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17 + RCT-Folly: 7b4f73a92ad9571b9dbdb05bb30fad927fa971e1 RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83 RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716 RCTTypeSafety: e7678bd60850ca5a41df9b8dc7154638cb66871f React: 4641770499c39f45d4e7cde1eba30e081f9d8a3d React-callinvoker: 4bef67b5c7f3f68db5929ab6a4d44b8a002998ea - React-Core: a68cea3e762814e60ecc3fa521c7f14c36c99245 - React-CoreModules: d81b1eaf8066add66299bab9d23c9f00c9484c7c - React-cxxreact: 984f8b1feeca37181d4e95301fcd6f5f6501c6ab + React-Core: 0a06707a0b34982efc4a556aff5dae4b22863455 + React-CoreModules: 907334e94314189c2e5eed4877f3efe7b26d85b0 + React-cxxreact: 3a1d5e8f4faa5e09be26614e9c8bbcae8d11b73d React-debug: 817160c07dc8d24d020fbd1eac7b3558ffc08964 - React-defaultsnativemodule: 18a684542f82ce1897552a1c4b847be414c9566e - React-domnativemodule: 90bdd4ec3ab38c47cfc3461c1e9283a8507d613f - React-Fabric: f6dade7007533daeb785ba5925039d83f343be4b - React-FabricComponents: b0655cc3e1b5ae12a4a1119aa7d8308f0ad33520 - React-FabricImage: 9b157c4c01ac2bf433f834f0e1e5fe234113a576 + React-defaultsnativemodule: 814830ccbc3fb08d67d0190e63b179ee4098c67b + React-domnativemodule: 270acf94bd0960b026bc3bfb327e703665d27fb4 + React-Fabric: 64586dc191fc1c170372a638b8e722e4f1d0a09b + React-FabricComponents: b0ebd032387468ea700574c581b139f57a7497fb + React-FabricImage: 81f0e0794caf25ad1224fa406d288fbc1986607f React-featureflags: f2792b067a351d86fdc7bec23db3b9a2f2c8d26c - React-featureflagsnativemodule: 742a8325b3c821d2a1ca13a6d2a0fc72d04555e0 - React-graphics: 68969e4e49d73f89da7abef4116c9b5f466aa121 - React-hermes: ac0bcba26a5d288ebc99b500e1097da2d0297ddf - React-idlecallbacksnativemodule: d61d9c9816131bf70d3d80cd04889fc625ee523f - React-ImageManager: e906eec93a9eb6102a06576b89d48d80a4683020 - React-jserrorhandler: ac5dde01104ff444e043cad8f574ca02756e20d6 - React-jsi: 496fa2b9d63b726aeb07d0ac800064617d71211d - React-jsiexecutor: dd22ab48371b80f37a0a30d0e8915b6d0f43a893 - React-jsinspector: 4629ac376f5765e684d19064f2093e55c97fd086 - React-jsitracing: 7a1c9cd484248870cf660733cd3b8114d54c035f - React-logger: c4052eb941cca9a097ef01b59543a656dc088559 - React-Mapbuffer: 33546a3ebefbccb8770c33a1f8a5554fa96a54de - React-microtasksnativemodule: d80ff86c8902872d397d9622f1a97aadcc12cead + React-featureflagsnativemodule: 0d7091ae344d6160c0557048e127897654a5c00f + React-graphics: cbebe910e4a15b65b0bff94a4d3ed278894d6386 + React-hermes: ec18c10f5a69d49fb9b5e17ae95494e9ea13d4d3 + React-idlecallbacksnativemodule: 6b84add48971da9c40403bd1860d4896462590f2 + React-ImageManager: f2a4c01c2ccb2193e60a20c135da74c7ca4d36f2 + React-jserrorhandler: 61d205b5a7cbc57fed3371dd7eed48c97f49fc64 + React-jsi: 95f7676103137861b79b0f319467627bcfa629ee + React-jsiexecutor: 41e0fe87cda9ea3970ffb872ef10f1ff8dbd1932 + React-jsinspector: 15578208796723e5c6f39069b6e8bf36863ef6e2 + React-jsitracing: 3758cdb155ea7711f0e77952572ea62d90c69f0b + React-logger: dbca7bdfd4aa5ef69431362bde6b36d49403cb20 + React-Mapbuffer: 6efad4a606c1fae7e4a93385ee096681ef0300dc + React-microtasksnativemodule: a645237a841d733861c70b69908ab4a1707b52ad React-nativeconfig: 8efdb1ef1e9158c77098a93085438f7e7b463678 - React-NativeModulesApple: cebca2e5320a3d66e123cade23bd90a167ffce5e - React-perflogger: 72e653eb3aba9122f9e57cf012d22d2486f33358 - React-performancetimeline: cd6a9374a72001165995d2ab632f672df04076dc + React-NativeModulesApple: 958d4f6c5c2ace4c0f427cf7ef82e28ae6538a22 + React-perflogger: 9b4f13c0afe56bc7b4a0e93ec74b1150421ee22d + React-performancetimeline: 359db1cb889aa0282fafc5838331b0987c4915a9 React-RCTActionSheet: aacf2375084dea6e7c221f4a727e579f732ff342 - React-RCTAnimation: 395ab53fd064dff81507c15efb781c8684d9a585 - React-RCTAppDelegate: 345a6f1b82abc578437df0ce7e9c48740eca827c - React-RCTBlob: 13311e554c1a367de063c10ee7c5e6573b2dd1d6 - React-RCTFabric: 007b1a98201cc49b5bc6e1417d7fe3f6fc6e2b78 - React-RCTImage: 1b1f914bcc12187c49ba5d949dac38c2eb9f5cc8 - React-RCTLinking: 4ac7c42beb65e36fba0376f3498f3cd8dd0be7fa - React-RCTNetwork: 938902773add4381e84426a7aa17a2414f5f94f7 - React-RCTSettings: e848f1ba17a7a18479cf5a31d28145f567da8223 - React-RCTText: 7e98fafdde7d29e888b80f0b35544e0cb07913cf - React-RCTVibration: cd7d80affd97dc7afa62f9acd491419558b64b78 + React-RCTAnimation: d8c82deebebe3aaf7a843affac1b57cb2dc073d4 + React-RCTAppDelegate: 1774aa421a29a41a704ecaf789811ef73c4634b6 + React-RCTBlob: 70a58c11a6a3500d1a12f2e51ca4f6c99babcff8 + React-RCTFabric: 731cda82aed592aacce2d32ead69d78cde5d9274 + React-RCTImage: 5e9d655ba6a790c31e3176016f9b47fd0978fbf0 + React-RCTLinking: 2a48338252805091f7521eaf92687206401bdf2a + React-RCTNetwork: 0c1282b377257f6b1c81934f72d8a1d0c010e4c3 + React-RCTSettings: f757b679a74e5962be64ea08d7865a7debd67b40 + React-RCTText: e7d20c490b407d3b4a2daa48db4bcd8ec1032af2 + React-RCTVibration: 8228e37144ca3122a91f1de16ba8e0707159cfec React-rendererconsistency: b4917053ecbaa91469c67a4319701c9dc0d40be6 - React-rendererdebug: aa181c36dd6cf5b35511d1ed875d6638fd38f0ec + React-rendererdebug: 81becbc8852b38d9b1b68672aa504556481330d5 React-rncore: 120d21715c9b4ba8f798bffe986cb769b988dd74 - React-RuntimeApple: d033becbbd1eba6f9f6e3af6f1893030ce203edd - React-RuntimeCore: 38af280bb678e66ba000a3c3d42920b2a138eebb + React-RuntimeApple: 52ed0e9e84a7c2607a901149fb13599a3c057655 + React-RuntimeCore: ca6189d2e53d86db826e2673fe8af6571b8be157 React-runtimeexecutor: 877596f82f5632d073e121cba2d2084b76a76899 - React-RuntimeHermes: 37aad735ff21ca6de2d8450a96de1afe9f86c385 - React-runtimescheduler: 8ec34cc885281a34696ea16c4fd86892d631f38d + React-RuntimeHermes: 3b752dc5d8a1661c9d1687391d6d96acfa385549 + React-runtimescheduler: 8321bb09175ace2a4f0b3e3834637eb85bf42ebe React-timing: 331cbf9f2668c67faddfd2e46bb7f41cbd9320b9 - React-utils: ed818f19ab445000d6b5c4efa9d462449326cc9f - ReactCodegen: f853a20cc9125c5521c8766b4b49375fec20648b - ReactCommon: 300d8d9c5cb1a6cd79a67cf5d8f91e4d477195f9 + React-utils: 54df9ada708578c8ad40d92895d6fed03e0e8a9e + ReactCodegen: 21a52ccddc6479448fc91903a437dd23ddc7366c + ReactCommon: bfd3600989d79bc3acbe7704161b171a1480b9fd SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a PODFILE CHECKSUM: 85124514d3acbe8b60389d46d3c38133e7c8e8de -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 51a923f5e..5c04bacb4 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,39 +1,39 @@ PODS: - boost (1.84.0) - - DatadogCore (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogCrashReporting (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogCore (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogCrashReporting (3.10.0): + - DatadogInternal (= 3.10.0) - KSCrash/Filters (= 2.5.0) - KSCrash/Recording (= 2.5.0) - - DatadogFlags (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogInternal (3.8.2) - - DatadogLogs (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogRUM (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogFlags (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogInternal (3.10.0) + - DatadogLogs (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogRUM (3.10.0): + - DatadogInternal (= 3.10.0) - DatadogSDKReactNative (3.3.0): - - DatadogCore (= 3.8.2) - - DatadogCrashReporting (= 3.8.2) - - DatadogFlags (= 3.8.2) - - DatadogLogs (= 3.8.2) - - DatadogRUM (= 3.8.2) - - DatadogTrace (= 3.8.2) - - DatadogWebViewTracking (= 3.8.2) + - DatadogCore (= 3.10.0) + - DatadogCrashReporting (= 3.10.0) + - DatadogFlags (= 3.10.0) + - DatadogLogs (= 3.10.0) + - DatadogRUM (= 3.10.0) + - DatadogTrace (= 3.10.0) + - DatadogWebViewTracking (= 3.10.0) - React-Core - DatadogSDKReactNative/Tests (3.3.0): - - DatadogCore (= 3.8.2) - - DatadogCrashReporting (= 3.8.2) - - DatadogFlags (= 3.8.2) - - DatadogLogs (= 3.8.2) - - DatadogRUM (= 3.8.2) - - DatadogTrace (= 3.8.2) - - DatadogWebViewTracking (= 3.8.2) + - DatadogCore (= 3.10.0) + - DatadogCrashReporting (= 3.10.0) + - DatadogFlags (= 3.10.0) + - DatadogLogs (= 3.10.0) + - DatadogRUM (= 3.10.0) + - DatadogTrace (= 3.10.0) + - DatadogWebViewTracking (= 3.10.0) - React-Core - DatadogSDKReactNativeSessionReplay (3.3.0): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.8.2) + - DatadogSessionReplay (= 3.10.0) - DoubleConversion - glog - hermes-engine @@ -56,7 +56,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay/Tests (3.3.0): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.8.2) + - DatadogSessionReplay (= 3.10.0) - DoubleConversion - glog - hermes-engine @@ -79,24 +79,24 @@ PODS: - ReactCommon/turbomodule/core - Yoga - DatadogSDKReactNativeWebView (3.3.0): - - DatadogInternal (= 3.8.2) + - DatadogInternal (= 3.10.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.8.2) + - DatadogWebViewTracking (= 3.10.0) - React-Core - DatadogSDKReactNativeWebView/Tests (3.3.0): - - DatadogInternal (= 3.8.2) + - DatadogInternal (= 3.10.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.8.2) + - DatadogWebViewTracking (= 3.10.0) - React-Core - react-native-webview - React-RCTText - - DatadogSessionReplay (3.8.2): - - DatadogInternal (= 3.8.2) - - DatadogTrace (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogSessionReplay (3.10.0): + - DatadogInternal (= 3.10.0) + - DatadogTrace (3.10.0): + - DatadogInternal (= 3.10.0) - OpenTelemetry-Swift-Api (~> 2.3.0) - - DatadogWebViewTracking (3.8.2): - - DatadogInternal (= 3.8.2) + - DatadogWebViewTracking (3.10.0): + - DatadogInternal (= 3.10.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -2004,18 +2004,18 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: 4375353a93311e99153ab3bc2fba10fefb295749 - DatadogCrashReporting: 4eedb27058432691a7e5f1bdcf3f72d8cc7c1a67 - DatadogFlags: fc732c5fa26259de55e83a13254522b38072d9d1 - DatadogInternal: a4640f67e249c9b4608e806cf1c8c10b17f66626 - DatadogLogs: 55e2c1c403e92f952f8ce451854c81f476b04d89 - DatadogRUM: 738b6b448df9af58ba0455451e628ac81040be04 - DatadogSDKReactNative: a9a30138e9ba851593da9e3ec593a36f1281b8a6 - DatadogSDKReactNativeSessionReplay: f59758e428d6cbf70ce553acfe1e6d8613c64411 - DatadogSDKReactNativeWebView: c10164944db2a090753afdd781a8a1af2c9b08b9 - DatadogSessionReplay: 4edcb9997acc8c378048932ffe0dd1e6bf2c18ca - DatadogTrace: faefc639bc2dd11a8ebbf9cbddf830b68eb62d1d - DatadogWebViewTracking: 50afac044e278ac0165c082b6b65dd8327fdefb4 + DatadogCore: be0f6c233bef27eda40acade29763943cb88935f + DatadogCrashReporting: 151f72523ee117000fed4b5e792de2349d38b331 + DatadogFlags: fbc1d17d2b83397c4f33817263a14b84a857ca1d + DatadogInternal: b81d08e4035de78140488b7422c239b17b89d251 + DatadogLogs: 20381f731050c500c85d36f4799cf0f28a56915e + DatadogRUM: 4f863c5330d85d6b54bb372d830929ad3b5006ab + DatadogSDKReactNative: 007959aa61bf705baa9160acee4f75107533c4e1 + DatadogSDKReactNativeSessionReplay: b383803a261b404bbc3b71744d2f86d001c9ce0c + DatadogSDKReactNativeWebView: 5fed9ac5c4807d00897fad86e26e2143bf0dba4f + DatadogSessionReplay: aef317ccd62a089691899bc7153177deec3c66d6 + DatadogTrace: c51a2bde28197cb1bab50031c83a82e3a1dbcf30 + DatadogWebViewTracking: 7775faa20f2044f2948c2eb246889ba5ceefbd6a DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 @@ -2025,72 +2025,72 @@ SPEC CHECKSUMS: HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352 KSCrash: 80e1e24eaefbe5134934ae11ca8d7746586bc2ed OpenTelemetry-Swift-Api: 3d77582ab6837a63b65bf7d2eacc57d8f2595edd - RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17 + RCT-Folly: 7b4f73a92ad9571b9dbdb05bb30fad927fa971e1 RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83 RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716 RCTTypeSafety: e7678bd60850ca5a41df9b8dc7154638cb66871f React: 4641770499c39f45d4e7cde1eba30e081f9d8a3d React-callinvoker: 4bef67b5c7f3f68db5929ab6a4d44b8a002998ea - React-Core: a68cea3e762814e60ecc3fa521c7f14c36c99245 - React-CoreModules: d81b1eaf8066add66299bab9d23c9f00c9484c7c - React-cxxreact: 984f8b1feeca37181d4e95301fcd6f5f6501c6ab + React-Core: 0a06707a0b34982efc4a556aff5dae4b22863455 + React-CoreModules: 907334e94314189c2e5eed4877f3efe7b26d85b0 + React-cxxreact: 3a1d5e8f4faa5e09be26614e9c8bbcae8d11b73d React-debug: 817160c07dc8d24d020fbd1eac7b3558ffc08964 - React-defaultsnativemodule: 21f216e8db975897eb32b5f13247f5bbfaa97f41 - React-domnativemodule: 19270ad4b8d33312838d257f24731a0026809d49 - React-Fabric: f6dade7007533daeb785ba5925039d83f343be4b - React-FabricComponents: b0655cc3e1b5ae12a4a1119aa7d8308f0ad33520 - React-FabricImage: 9b157c4c01ac2bf433f834f0e1e5fe234113a576 + React-defaultsnativemodule: a965cb39fb0a79276ab611793d39f52e59a9a851 + React-domnativemodule: d647f94e503c62c44f54291334b1aa22a30fa08b + React-Fabric: 64586dc191fc1c170372a638b8e722e4f1d0a09b + React-FabricComponents: b0ebd032387468ea700574c581b139f57a7497fb + React-FabricImage: 81f0e0794caf25ad1224fa406d288fbc1986607f React-featureflags: f2792b067a351d86fdc7bec23db3b9a2f2c8d26c - React-featureflagsnativemodule: 3a8731d8fd9f755be57e00d9fa8a7f92aa77e87d - React-graphics: 68969e4e49d73f89da7abef4116c9b5f466aa121 - React-hermes: ac0bcba26a5d288ebc99b500e1097da2d0297ddf - React-idlecallbacksnativemodule: 9a2c5b5c174c0c476f039bedc1b9497a8272133e - React-ImageManager: e906eec93a9eb6102a06576b89d48d80a4683020 - React-jserrorhandler: ac5dde01104ff444e043cad8f574ca02756e20d6 - React-jsi: 496fa2b9d63b726aeb07d0ac800064617d71211d - React-jsiexecutor: dd22ab48371b80f37a0a30d0e8915b6d0f43a893 - React-jsinspector: 4629ac376f5765e684d19064f2093e55c97fd086 - React-jsitracing: 7a1c9cd484248870cf660733cd3b8114d54c035f - React-logger: c4052eb941cca9a097ef01b59543a656dc088559 - React-Mapbuffer: 33546a3ebefbccb8770c33a1f8a5554fa96a54de - React-microtasksnativemodule: 5c3d795318c22ab8df55100e50b151384a4a60b3 - react-native-crash-tester: 48bde9d6f5256c61ef2e0c52dfc74256b26e55eb - react-native-safe-area-context: e134b241010ebe2aacdcea013565963d13826faa - react-native-webview: 2ea635bc43fd8a4b89de61133e8cc0607084e9f8 + React-featureflagsnativemodule: 95a02d895475de8ace78fedd76143866838bb720 + React-graphics: cbebe910e4a15b65b0bff94a4d3ed278894d6386 + React-hermes: ec18c10f5a69d49fb9b5e17ae95494e9ea13d4d3 + React-idlecallbacksnativemodule: 0c1ae840cc5587197cd926a3cb76828ad059d116 + React-ImageManager: f2a4c01c2ccb2193e60a20c135da74c7ca4d36f2 + React-jserrorhandler: 61d205b5a7cbc57fed3371dd7eed48c97f49fc64 + React-jsi: 95f7676103137861b79b0f319467627bcfa629ee + React-jsiexecutor: 41e0fe87cda9ea3970ffb872ef10f1ff8dbd1932 + React-jsinspector: 15578208796723e5c6f39069b6e8bf36863ef6e2 + React-jsitracing: 3758cdb155ea7711f0e77952572ea62d90c69f0b + React-logger: dbca7bdfd4aa5ef69431362bde6b36d49403cb20 + React-Mapbuffer: 6efad4a606c1fae7e4a93385ee096681ef0300dc + React-microtasksnativemodule: 8732b71aa66045da4bb341ddee1bb539f71e5f38 + react-native-crash-tester: 3ffaa64141427ca362079cb53559fe9a532487ae + react-native-safe-area-context: 04803a01f39f31cc6605a5531280b477b48f8a88 + react-native-webview: 1e12de2fad74c17b4f8b1b53ebd1e3baa0148d71 React-nativeconfig: 8efdb1ef1e9158c77098a93085438f7e7b463678 - React-NativeModulesApple: cebca2e5320a3d66e123cade23bd90a167ffce5e - React-perflogger: 72e653eb3aba9122f9e57cf012d22d2486f33358 - React-performancetimeline: cd6a9374a72001165995d2ab632f672df04076dc + React-NativeModulesApple: 958d4f6c5c2ace4c0f427cf7ef82e28ae6538a22 + React-perflogger: 9b4f13c0afe56bc7b4a0e93ec74b1150421ee22d + React-performancetimeline: 359db1cb889aa0282fafc5838331b0987c4915a9 React-RCTActionSheet: aacf2375084dea6e7c221f4a727e579f732ff342 - React-RCTAnimation: 395ab53fd064dff81507c15efb781c8684d9a585 - React-RCTAppDelegate: 1e5b43833e3e36e9fa34eec20be98174bc0e14a2 - React-RCTBlob: 13311e554c1a367de063c10ee7c5e6573b2dd1d6 - React-RCTFabric: bd906861a4e971e21d8df496c2d8f3ca6956f840 - React-RCTImage: 1b1f914bcc12187c49ba5d949dac38c2eb9f5cc8 - React-RCTLinking: 4ac7c42beb65e36fba0376f3498f3cd8dd0be7fa - React-RCTNetwork: 938902773add4381e84426a7aa17a2414f5f94f7 - React-RCTSettings: e848f1ba17a7a18479cf5a31d28145f567da8223 - React-RCTText: 7e98fafdde7d29e888b80f0b35544e0cb07913cf - React-RCTVibration: cd7d80affd97dc7afa62f9acd491419558b64b78 + React-RCTAnimation: d8c82deebebe3aaf7a843affac1b57cb2dc073d4 + React-RCTAppDelegate: 6c0377d9c4058773ea7073bb34bb9ebd6ddf5a84 + React-RCTBlob: 70a58c11a6a3500d1a12f2e51ca4f6c99babcff8 + React-RCTFabric: 7eb6dd2c8fda98cb860a572e3f4e4eb60d62c89e + React-RCTImage: 5e9d655ba6a790c31e3176016f9b47fd0978fbf0 + React-RCTLinking: 2a48338252805091f7521eaf92687206401bdf2a + React-RCTNetwork: 0c1282b377257f6b1c81934f72d8a1d0c010e4c3 + React-RCTSettings: f757b679a74e5962be64ea08d7865a7debd67b40 + React-RCTText: e7d20c490b407d3b4a2daa48db4bcd8ec1032af2 + React-RCTVibration: 8228e37144ca3122a91f1de16ba8e0707159cfec React-rendererconsistency: b4917053ecbaa91469c67a4319701c9dc0d40be6 - React-rendererdebug: aa181c36dd6cf5b35511d1ed875d6638fd38f0ec + React-rendererdebug: 81becbc8852b38d9b1b68672aa504556481330d5 React-rncore: 120d21715c9b4ba8f798bffe986cb769b988dd74 - React-RuntimeApple: d033becbbd1eba6f9f6e3af6f1893030ce203edd - React-RuntimeCore: 38af280bb678e66ba000a3c3d42920b2a138eebb + React-RuntimeApple: 52ed0e9e84a7c2607a901149fb13599a3c057655 + React-RuntimeCore: ca6189d2e53d86db826e2673fe8af6571b8be157 React-runtimeexecutor: 877596f82f5632d073e121cba2d2084b76a76899 - React-RuntimeHermes: 37aad735ff21ca6de2d8450a96de1afe9f86c385 - React-runtimescheduler: 8ec34cc885281a34696ea16c4fd86892d631f38d + React-RuntimeHermes: 3b752dc5d8a1661c9d1687391d6d96acfa385549 + React-runtimescheduler: 8321bb09175ace2a4f0b3e3834637eb85bf42ebe React-timing: 331cbf9f2668c67faddfd2e46bb7f41cbd9320b9 - React-utils: ed818f19ab445000d6b5c4efa9d462449326cc9f - ReactCodegen: f853a20cc9125c5521c8766b4b49375fec20648b - ReactCommon: 300d8d9c5cb1a6cd79a67cf5d8f91e4d477195f9 - ReactNativeNavigation: 445f86273eb245d15b14023ee4ef9d6e4f891ad6 - RNCAsyncStorage: b44e8a4e798c3e1f56bffccd0f591f674fb9198f - RNGestureHandler: cb711d56ee3b03a5adea1d38324d4459ab55653f - RNScreens: f75b26fd4777848c216e27b0a09e1bf9c9f4760a + React-utils: 54df9ada708578c8ad40d92895d6fed03e0e8a9e + ReactCodegen: 21a52ccddc6479448fc91903a437dd23ddc7366c + ReactCommon: bfd3600989d79bc3acbe7704161b171a1480b9fd + ReactNativeNavigation: 50c1eef68b821e7265eff3a391d27ed18fdce459 + RNCAsyncStorage: 23e56519cc41d3bade3c8d4479f7760cb1c11996 + RNGestureHandler: 950dfa674dbf481460ca389c65b9036ac4ab8ada + RNScreens: 606ab1cf68162f7ba0d049a31f2a84089a6fffb4 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a PODFILE CHECKSUM: a2729cbf6c0a559932ef8f49d27110873b77ff7d -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/packages/core/DatadogSDKReactNative.podspec b/packages/core/DatadogSDKReactNative.podspec index 35483787e..351ec31b5 100644 --- a/packages/core/DatadogSDKReactNative.podspec +++ b/packages/core/DatadogSDKReactNative.podspec @@ -19,15 +19,15 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the versions in sync with DatadogSDKReactNativeSessionReplay.podspec - s.dependency 'DatadogCore', '3.8.2' - s.dependency 'DatadogLogs', '3.8.2' - s.dependency 'DatadogTrace', '3.8.2' - s.dependency 'DatadogRUM', '3.8.2' - s.dependency 'DatadogCrashReporting', '3.8.2' - s.dependency 'DatadogFlags', '3.8.2' + s.dependency 'DatadogCore', '3.10.0' + s.dependency 'DatadogLogs', '3.10.0' + s.dependency 'DatadogTrace', '3.10.0' + s.dependency 'DatadogRUM', '3.10.0' + s.dependency 'DatadogCrashReporting', '3.10.0' + s.dependency 'DatadogFlags', '3.10.0' # DatadogWebViewTracking is not available for tvOS - s.ios.dependency 'DatadogWebViewTracking', '3.8.2' + s.ios.dependency 'DatadogWebViewTracking', '3.10.0' s.test_spec 'Tests' do |test_spec| test_spec.source_files = 'ios/Tests/**/*.{swift,json}' diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt index cf83019dd..692f5a840 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt @@ -119,12 +119,16 @@ class DdRumImplementation(private val datadog: DatadogWrapper = DatadogSDKWrappe * Add a RUM Action. * @param type The action type (tap, scroll, swipe, click, custom). * @param name The action name. + * @param touch The native touch data for tap actions, or null for other action types. + * Currently unused on Android; reserved for heatmap support. * @param context The additional context to send. * @param timestampMs The timestamp when the action occurred (in milliseconds). If not provided, current timestamp will be used. */ + @Suppress("LongParameterList", "UnusedParameter") fun addAction( type: String, name: String, + touch: ReadableMap?, context: ReadableMap, timestampMs: Double, promise: Promise diff --git a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt index 30788acff..e7d80ff25 100644 --- a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt +++ b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt @@ -104,19 +104,23 @@ class DdRum( * Add a RUM Action. * @param type The action type (tap, scroll, swipe, click, custom). * @param name The action name. + * @param touch The native touch data for tap actions, or null for other action types. + * Currently unused on Android; reserved for heatmap support. * @param context The additional context to send. * @param timestampMs The timestamp when the action occurred (in milliseconds). * If not provided, current timestamp will be used. */ + @Suppress("LongParameterList") @ReactMethod override fun addAction( type: String, name: String, + touch: ReadableMap?, context: ReadableMap, timestampMs: Double, promise: Promise ) { - implementation.addAction(type, name, context, timestampMs, promise) + implementation.addAction(type, name, touch, context, timestampMs, promise) } /** diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt index 4ef8409b2..dc966dce1 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt @@ -97,18 +97,22 @@ class DdRum( * Add a RUM Action. * @param type The action type (tap, scroll, swipe, click, custom). * @param name The action name. + * @param touch The native touch data for tap actions, or null for other action types. + * Currently unused on Android; reserved for heatmap support. * @param context The additional context to send. * @param timestampMs The timestamp when the action occurred (in milliseconds). If not provided, current timestamp will be used. */ + @Suppress("LongParameterList") @ReactMethod fun addAction( type: String, name: String, + touch: ReadableMap?, context: ReadableMap, timestampMs: Double, promise: Promise ) { - implementation.addAction(type, name, context, timestampMs, promise) + implementation.addAction(type, name, touch, context, timestampMs, promise) } /** diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt index 9619794df..d5315fa8b 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt @@ -137,6 +137,7 @@ internal class DdRumTest { testedDdRum.addAction( type.name, name, + null, fakeContext.toReadableMap(), fakeTimestamp, mockPromise @@ -157,7 +158,14 @@ internal class DdRumTest { } // When - testedDdRum.addAction(type, name, fakeContext.toReadableMap(), fakeTimestamp, mockPromise) + testedDdRum.addAction( + type, + name, + null, + fakeContext.toReadableMap(), + fakeTimestamp, + mockPromise + ) // Then verify(mockRumMonitor).addAction(RumActionType.CUSTOM, name, updatedContext) diff --git a/packages/core/ios/Sources/DdRum.mm b/packages/core/ios/Sources/DdRum.mm index c891537f3..58c9938cf 100644 --- a/packages/core/ios/Sources/DdRum.mm +++ b/packages/core/ios/Sources/DdRum.mm @@ -14,6 +14,7 @@ @implementation DdRum +@synthesize bridge = _bridge; RCT_EXPORT_MODULE() RCT_REMAP_METHOD(startView, withKey:(NSString*)key @@ -57,12 +58,13 @@ @implementation DdRum RCT_REMAP_METHOD(addAction, addWithType:(NSString*)type withName:(NSString*)name + withTouch:(nullable NSDictionary*)touch withContext:(NSDictionary*)context withTimestampms:(double)timestampMs withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) { - [self addAction:type name:name context:context timestampMs:timestampMs resolve:resolve reject:reject]; + [self addAction:type name:name touch:touch context:context timestampMs:timestampMs resolve:resolve reject:reject]; } RCT_REMAP_METHOD(startResource, withKey:(NSString*)key @@ -207,7 +209,7 @@ @implementation DdRum - (DdRumImplementation*)ddRumImplementation { if (_ddRumImplementation == nil) { - _ddRumImplementation = [[DdRumImplementation alloc] init]; + _ddRumImplementation = [[DdRumImplementation alloc] initWithBridge:_bridge]; } return _ddRumImplementation; } @@ -220,8 +222,8 @@ - (dispatch_queue_t)methodQueue { return [RNQueue getSharedQueue]; } -- (void)addAction:(NSString *)type name:(NSString *)name context:(NSDictionary *)context timestampMs:(double)timestampMs resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - [self.ddRumImplementation addActionWithType:type name:name context:context timestampMs:timestampMs resolve:resolve reject:reject]; +- (void)addAction:(NSString *)type name:(NSString *)name touch:(nullable NSDictionary *)touch context:(NSDictionary *)context timestampMs:(double)timestampMs resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddRumImplementation addActionWithType:type name:name touch:touch context:context timestampMs:timestampMs resolve:resolve reject:reject]; } - (void)addError:(NSString *)message source:(NSString *)source stacktrace:(NSString *)stacktrace context:(NSDictionary *)context timestampMs:(double)timestampMs fingerprint:(NSString *)fingerprint resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { diff --git a/packages/core/ios/Sources/DdRumImplementation.swift b/packages/core/ios/Sources/DdRumImplementation.swift index 1913de3ea..c83098949 100644 --- a/packages/core/ios/Sources/DdRumImplementation.swift +++ b/packages/core/ios/Sources/DdRumImplementation.swift @@ -8,6 +8,7 @@ import Foundation @_spi(Experimental) import DatadogRUM import DatadogInternal +import React private extension RUMActionType { init(from string: String) { @@ -91,24 +92,37 @@ public class DdRumImplementation: NSObject { lazy var nativeRUM: RUMMonitorProtocol = rumProvider() lazy var rumInternal: RUMMonitorInternalProtocol? = rumInternalProvider() + lazy var heatmapIdentifierRegistry: HeatmapIdentifierRegistry? = heatmapIdentifierRegistryProvider() + private let mainDispatchQueue: DispatchQueueType + private let uiManager: RCTUIManager + private let heatmapIdentifierRegistryProvider: () -> HeatmapIdentifierRegistry? private let rumProvider: () -> RUMMonitorProtocol private let rumInternalProvider: () -> RUMMonitorInternalProtocol? private typealias UserAction = (type: RUMActionType, name: String?) internal init( - _ rumProvider: @escaping () -> RUMMonitorProtocol, - _ rumInternalProvider: @escaping () -> RUMMonitorInternalProtocol? + mainDispatchQueue: DispatchQueueType, + uiManager: RCTUIManager, + heatmapIdentifierRegistryProvider: @escaping () -> HeatmapIdentifierRegistry?, + rumProvider: @escaping () -> RUMMonitorProtocol, + rumInternalProvider: @escaping () -> RUMMonitorInternalProtocol? ) { + self.mainDispatchQueue = mainDispatchQueue + self.uiManager = uiManager + self.heatmapIdentifierRegistryProvider = heatmapIdentifierRegistryProvider self.rumProvider = rumProvider self.rumInternalProvider = rumInternalProvider } @objc - public override convenience init() { + public convenience init(bridge: RCTBridge) { self.init( - { RUMMonitor.shared() }, - { RUMMonitor.shared()._internal } + mainDispatchQueue: DispatchQueue.main, + uiManager: bridge.uiManager, + heatmapIdentifierRegistryProvider: { CoreRegistry.default.heatmapIdentifierRegistry }, + rumProvider: { RUMMonitor.shared() }, + rumInternalProvider: { RUMMonitor.shared()._internal } ) } @@ -137,8 +151,24 @@ public class DdRumImplementation: NSObject { } @objc - public func addAction(type: String, name: String, context: NSDictionary, timestampMs: Double, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - nativeRUM.addAction(type: RUMActionType(from: type), name: name, attributes: attributes(from: context, with: timestampMs)) + public func addAction(type: String, name: String, touch: NSDictionary?, context: NSDictionary, timestampMs: Double, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + if + let touch, + let reactTag = touch["reactTag"] as? NSNumber, + let x = touch["x"] as? NSNumber, + let y = touch["y"] as? NSNumber + { + addAction( + at: Date(timeIntervalSince1970: timestampMs / 1_000), + type: RUMActionType(from: type), + name: name, + reactTag: reactTag, + location: .init(x: CGFloat(truncating: x), y: CGFloat(truncating: y)), + attributes: castAttributesToSwift(context) + ) + } else { + nativeRUM.addAction(type: RUMActionType(from: type), name: name, attributes: attributes(from: context, with: timestampMs)) + } resolve(nil) } @@ -326,6 +356,35 @@ public class DdRumImplementation: NSObject { } } + private func addAction( + at time: Date, + type: RUMActionType, + name: String, + reactTag: NSNumber, + location: CGPoint, + attributes: [AttributeKey: AttributeValue] + ) { + mainDispatchQueue.async { [uiManager, heatmapIdentifierRegistry, rumInternal] in + let heatmapAttributes: HeatmapAttributes? = uiManager.view(forReactTag: reactTag).flatMap { view in + guard let identifier = heatmapIdentifierRegistry?.heatmapIdentifier(for: ObjectIdentifier(view)) else { + return nil + } + return HeatmapAttributes( + identifier: identifier, + size: view.bounds.size, + location: location + ) + } + rumInternal?.addAction( + at: time, + type: type, + name: name, + heatmapAttributes: heatmapAttributes, + attributes: attributes + ) + } + } + private func timingValue(from timings: [String: Any], for timingName: String) -> (start: Date, end: Date)? { let timing = timings[timingName] as? [String: NSNumber] if let startInNs = timing?["startTime"]?.int64Value, let durationInNs = timing?["duration"]?.int64Value { diff --git a/packages/core/ios/Sources/RUMMonitorInternalProtocol.swift b/packages/core/ios/Sources/RUMMonitorInternalProtocol.swift index 4c6492bb4..62cfb101f 100644 --- a/packages/core/ios/Sources/RUMMonitorInternalProtocol.swift +++ b/packages/core/ios/Sources/RUMMonitorInternalProtocol.swift @@ -42,6 +42,14 @@ public protocol RUMMonitorInternalProtocol { requestBodySize: (encoded: Int64, decoded: Int64)?, attributes: [AttributeKey: AttributeValue] ) + + func addAction( + at time: Date, + type: RUMActionType, + name: String, + heatmapAttributes: HeatmapAttributes?, + attributes: [AttributeKey: AttributeValue] + ) } extension DatadogInternalInterface: RUMMonitorInternalProtocol {} diff --git a/packages/core/ios/Tests/DdRumTests.swift b/packages/core/ios/Tests/DdRumTests.swift index 3c711be99..cc9ff5751 100644 --- a/packages/core/ios/Tests/DdRumTests.swift +++ b/packages/core/ios/Tests/DdRumTests.swift @@ -9,9 +9,12 @@ import XCTest @testable import DatadogRUM @testable import DatadogSDKReactNative @testable import DatadogInternal +import React internal class DdRumTests: XCTestCase { private let mockNativeRUM = MockRUMMonitor() + private let mockUIManager = MockUIManager() + private let mockHeatmapIdentifierRegistry = MockHeatmapIdentifierRegistry() private var rum: DdRumImplementation! // swiftlint:disable:this implicitly_unwrapped_optional private func mockResolve(args: Any?) {} @@ -21,17 +24,29 @@ internal class DdRumTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - rum = DdRumImplementation({ self.mockNativeRUM }, { self.mockNativeRUM._internalMock }) + rum = DdRumImplementation( + mainDispatchQueue: DispatchQueueMock(), + uiManager: self.mockUIManager, + heatmapIdentifierRegistryProvider: { self.mockHeatmapIdentifierRegistry }, + rumProvider: { self.mockNativeRUM }, + rumInternalProvider: { self.mockNativeRUM._internalMock } + ) } func testItInitializesNativeRumOnlyOnce() { // Given let expectation = self.expectation(description: "Initialize RUM once") - let rum = DdRumImplementation({ [unowned self] in - expectation.fulfill() - return self.mockNativeRUM - }, { nil }) + let rum = DdRumImplementation( + mainDispatchQueue: DispatchQueueMock(), + uiManager: MockUIManager(), + heatmapIdentifierRegistryProvider: { nil }, + rumProvider: { [unowned self] in + expectation.fulfill() + return self.mockNativeRUM + }, + rumInternalProvider: { nil } + ) // When (0..<10).forEach { _ in rum.addTiming(name: "foo", resolve: mockResolve, reject: mockReject) } @@ -103,7 +118,7 @@ internal class DdRumTests: XCTestCase { } func testAddAction() throws { - rum.addAction(type: "scroll", name: "action name", context: ["foo": 123], timestampMs: randomTimestamp, resolve: mockResolve, reject: mockReject) + rum.addAction(type: "scroll", name: "action name", touch: nil, context: ["foo": 123], timestampMs: randomTimestamp, resolve: mockResolve, reject: mockReject) XCTAssertEqual(mockNativeRUM.calledMethods.count, 1) XCTAssertEqual(mockNativeRUM.calledMethods.last, .addUserAction(type: .scroll, name: "action name")) @@ -114,6 +129,78 @@ internal class DdRumTests: XCTestCase { XCTAssertEqual(lastAttributes[DdRumImplementation.timestampKey] as? Int64, Int64(randomTimestamp)) } + func testAddActionWithTouch() throws { + // Given + let view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 50)) + let reactTag = NSNumber(value: 42) + let identifier = HeatmapIdentifier(rawValue: "abc123") + + mockUIManager.views[reactTag] = view + mockHeatmapIdentifierRegistry.identifiers[ObjectIdentifier(view)] = identifier + + let touch: NSDictionary = [ + "reactTag": 42, + "x": 10.0, + "y": 20.0 + ] + + // When + rum.addAction( + type: "tap", + name: "tap action", + touch: touch, + context: [:], + timestampMs: randomTimestamp, + resolve: mockResolve, + reject: mockReject + ) + + // Then + XCTAssertEqual(mockNativeRUM.calledMethods.count, 1) + XCTAssertEqual( + mockNativeRUM.calledMethods.last, + .addAction( + time: Date(timeIntervalSince1970: randomTimestamp / 1_000), + type: .tap, + name: "tap action", + heatmapAttributes: HeatmapAttributes( + identifier: identifier, + size: CGSize(width: 200, height: 50), + location: CGPoint(x: 10, y: 20) + ) + ) + ) + } + + func testAddActionWithTouchAndUnknownReactTag() throws { + let touch: NSDictionary = [ + "reactTag": 999, + "x": 10.0, + "y": 20.0 + ] + + rum.addAction( + type: "tap", + name: "tap action", + touch: touch, + context: [:], + timestampMs: randomTimestamp, + resolve: mockResolve, + reject: mockReject + ) + + XCTAssertEqual(mockNativeRUM.calledMethods.count, 1) + XCTAssertEqual( + mockNativeRUM.calledMethods.last, + .addAction( + time: Date(timeIntervalSince1970: randomTimestamp / 1_000), + type: .tap, + name: "tap action", + heatmapAttributes: nil + ) + ) + } + func testStartResource() throws { rum.startResource(key: "resource key", method: "put", url: "some/url/string", context: ["foo": 123], timestampMs: randomTimestamp, resolve: mockResolve, reject: mockReject) @@ -348,3 +435,24 @@ internal class DdRumTests: XCTestCase { return Date(timeIntervalSince1970: TimeInterval(fromNs: timestampNs)) } } + +private class MockUIManager: RCTUIManager { + var views: [NSNumber: UIView] = [:] + + override func view(forReactTag reactTag: NSNumber!) -> UIView? { + views[reactTag] + } +} + +private final class MockHeatmapIdentifierRegistry: @unchecked Sendable, HeatmapIdentifierRegistry { + @ReadWriteLock + var identifiers: [ObjectIdentifier: HeatmapIdentifier] = [:] + + func setHeatmapIdentifiers(_ heatmapIdentifiers: [ObjectIdentifier: HeatmapIdentifier]) { + identifiers = heatmapIdentifiers + } + + func heatmapIdentifier(for objectIdentifier: ObjectIdentifier) -> HeatmapIdentifier? { + identifiers[objectIdentifier] + } +} diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index 3a7224e38..ce1d2c6cd 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -82,6 +82,7 @@ internal class MockRUMMonitor: RUMMonitorProtocol { requestBodySize: BodySize?) case addLongTasks(time: Date, duration: TimeInterval) case updatePerformanceMetric(time: Date, metric: DatadogRUM.PerformanceMetric, value: Double) + case addAction(time: Date, type: RUMActionType, name: String, heatmapAttributes: HeatmapAttributes?) } public var calledMethods = [CalledMethod]() @@ -219,4 +220,22 @@ public struct MockRUMMonitorInternal: RUMMonitorInternalProtocol { ) monitor.receivedAttributes.append(attributes) } + + public func addAction( + at time: Date, + type: RUMActionType, + name: String, + heatmapAttributes: HeatmapAttributes?, + attributes: [AttributeKey : any AttributeValue] + ) { + monitor.calledMethods.append( + .addAction( + time: time, + type: type, + name: name, + heatmapAttributes: heatmapAttributes + ) + ) + monitor.receivedAttributes.append(attributes) + } } diff --git a/packages/core/src/__tests__/DdSdkReactNative.test.tsx b/packages/core/src/__tests__/DdSdkReactNative.test.tsx index d9abeafe2..2ec1d2233 100644 --- a/packages/core/src/__tests__/DdSdkReactNative.test.tsx +++ b/packages/core/src/__tests__/DdSdkReactNative.test.tsx @@ -885,6 +885,7 @@ describe('DdSdkReactNative', () => { expect(NativeModules.DdRum.addAction).toHaveBeenCalledWith( 'CUSTOM', 'Click on button', + null, { body: 'content' }, diff --git a/packages/core/src/__tests__/rum/instrumentation/DdRumUserInteractionTracking.test.tsx b/packages/core/src/__tests__/rum/instrumentation/DdRumUserInteractionTracking.test.tsx index 6ee9e9c71..61edf5664 100644 --- a/packages/core/src/__tests__/rum/instrumentation/DdRumUserInteractionTracking.test.tsx +++ b/packages/core/src/__tests__/rum/instrumentation/DdRumUserInteractionTracking.test.tsx @@ -73,6 +73,7 @@ it('M intercept and send a RUM event W onPress { Button component }', async () = expect(DdRum.addAction).toBeCalledWith( 'TAP', 'click_me_button', + null, expect.anything(), expect.anything() ); @@ -105,6 +106,7 @@ it('M intercept and send a RUM event with elementType W onPress { Button compone expect(DdRum.addAction).toBeCalledWith( 'TAP', 'test_element_type', + null, expect.anything(), expect.anything() ); @@ -136,6 +138,7 @@ it('M intercept and send a RUM event W onPress { custom action name prop used }' expect(DdRum.addAction).toBeCalledWith( 'TAP', 'click_me_test_ID', + null, expect.anything(), expect.anything() ); @@ -166,6 +169,7 @@ it('M intercept only once W startTracking { called multiple times }', async () = expect(DdRum.addAction).toBeCalledWith( 'TAP', 'click_me_button', + null, expect.anything(), expect.anything() ); @@ -196,6 +200,7 @@ it('M intercept and send a RUM event W onPress { TouchableOpacity component }', expect(DdRum.addAction).toBeCalledWith( 'TAP', 'click_me_button', + null, expect.anything(), expect.anything() ); @@ -228,6 +233,7 @@ it('M intercept and send a RUM event W onPress { TouchableHighlight component }' expect(DdRum.addAction).toBeCalledWith( 'TAP', 'click_me_button', + null, expect.anything(), expect.anything() ); @@ -260,6 +266,7 @@ it('M intercept and send a RUM event W onPress { TouchableNativeFeedback compone expect(DdRum.addAction).toBeCalledWith( 'TAP', 'click_me_button', + null, expect.anything(), expect.anything() ); @@ -292,6 +299,7 @@ it('M intercept and send a RUM event W onPress { TouchableWithoutFeedback compon expect(DdRum.addAction).toBeCalledWith( 'TAP', 'click_me_button', + null, expect.anything(), expect.anything() ); diff --git a/packages/core/src/nativeModulesTypes.ts b/packages/core/src/nativeModulesTypes.ts index d2d42297a..64b2ad952 100644 --- a/packages/core/src/nativeModulesTypes.ts +++ b/packages/core/src/nativeModulesTypes.ts @@ -95,12 +95,14 @@ export interface DdNativeRumType extends NativeDdRum { * Add a RUM Action. * @param type: The action type (tap, scroll, swipe, back, custom). * @param name: The action name. + * @param touch: The native touch data for tap actions, or null for other action types. * @param context: The additional context to send. * @param timestampMs: The timestamp when the action occurred (in milliseconds). If not provided, current timestamp will be used. */ addAction( type: ActionType, name: string, + touch: object | null, context: object, timestampMs: number ): Promise; diff --git a/packages/core/src/rum/DdRum.ts b/packages/core/src/rum/DdRum.ts index 4241f9282..120436950 100644 --- a/packages/core/src/rum/DdRum.ts +++ b/packages/core/src/rum/DdRum.ts @@ -52,6 +52,26 @@ const RUM_MODULE = 'com.datadog.reactnative.rum'; const generateEmptyPromise = () => new Promise(resolve => resolve()); +type TouchData = { + reactTag: number; + x: number; + y: number; +}; + +const touchDataFromEvent = ( + event?: GestureResponderEvent +): TouchData | null => { + const nativeEvent = event?.nativeEvent; + if (!nativeEvent) { + return null; + } + return { + reactTag: Number(nativeEvent.target), + x: nativeEvent.locationX, + y: nativeEvent.locationY + }; +}; + class DdRumWrapper implements DdRumType { // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires private nativeRum: DdNativeRumType = require('../specs/NativeDdRum') @@ -215,6 +235,7 @@ class DdRumWrapper implements DdRumType { this.nativeRum.addAction( mappedEvent.type, mappedEvent.name, + touchDataFromEvent(mappedEvent.actionContext), encodeAttributes(mappedEvent.context), mappedEvent.timestampMs ) diff --git a/packages/core/src/rum/__tests__/DdRum.test.ts b/packages/core/src/rum/__tests__/DdRum.test.ts index 67e7d8683..2507b04e2 100644 --- a/packages/core/src/rum/__tests__/DdRum.test.ts +++ b/packages/core/src/rum/__tests__/DdRum.test.ts @@ -6,6 +6,7 @@ */ import { NativeModules } from 'react-native'; +import type { GestureResponderEvent } from 'react-native'; import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../config/types'; @@ -1285,6 +1286,7 @@ describe('DdRum', () => { expect(NativeModules.DdRum.addAction).toHaveBeenCalledWith( expect.anything(), expect.anything(), + null, context, expect.anything() ); @@ -1303,6 +1305,7 @@ describe('DdRum', () => { expect(NativeModules.DdRum.addAction).toHaveBeenCalledWith( expect.anything(), expect.anything(), + null, {}, expect.anything() ); @@ -1321,6 +1324,7 @@ describe('DdRum', () => { expect(NativeModules.DdRum.addAction).toHaveBeenCalledWith( expect.anything(), expect.anything(), + null, { context }, expect.anything() ); @@ -1704,6 +1708,32 @@ describe('DdRum', () => { }); describe('DdRum.addAction', () => { + it('passes touch data from GestureResponderEvent to native module', async () => { + const event = ({ + nativeEvent: { + target: 42, + locationX: 10, + locationY: 20 + } + } as unknown) as GestureResponderEvent; + + await DdRum.addAction( + RumActionType.TAP, + 'tap button', + {}, + undefined, + event + ); + + expect(NativeModules.DdRum.addAction).toHaveBeenCalledWith( + 'TAP', + 'tap button', + { reactTag: 42, x: 10, y: 20 }, + {}, + expect.anything() + ); + }); + it('registers event mapper and maps action', async () => { const actionEventMapper: ActionEventMapper = action => { action.context = { frustration: true }; @@ -1720,6 +1750,7 @@ describe('DdRum', () => { expect(NativeModules.DdRum.addAction).toHaveBeenCalledWith( 'CUSTOM', 'Click on button', + null, { frustration: true }, diff --git a/packages/core/src/rum/eventMappers/actionEventMapper.ts b/packages/core/src/rum/eventMappers/actionEventMapper.ts index b5372951a..c055032f4 100644 --- a/packages/core/src/rum/eventMappers/actionEventMapper.ts +++ b/packages/core/src/rum/eventMappers/actionEventMapper.ts @@ -25,6 +25,7 @@ type NativeAction = { name: string; context: object; timestampMs: number; + actionContext?: GestureResponderEvent; }; export type ActionEventMapper = (event: ActionEvent) => ActionEvent | null; diff --git a/packages/core/src/rum/instrumentation/interactionTracking/DdBabelInteractionTracking.ts b/packages/core/src/rum/instrumentation/interactionTracking/DdBabelInteractionTracking.ts index 124ba87e9..4cb146783 100644 --- a/packages/core/src/rum/instrumentation/interactionTracking/DdBabelInteractionTracking.ts +++ b/packages/core/src/rum/instrumentation/interactionTracking/DdBabelInteractionTracking.ts @@ -142,7 +142,8 @@ class BabelInteractionTracking { action, targetName, { '__dd.action_source': ActionSource.BABEL }, - this.timeProvider.now() + this.timeProvider.now(), + args[0] ) .catch(e => { if (e instanceof Error) { diff --git a/packages/core/src/rum/instrumentation/interactionTracking/__tests__/DdBabelInteractionTracking.test.ts b/packages/core/src/rum/instrumentation/interactionTracking/__tests__/DdBabelInteractionTracking.test.ts index a5ffa9d32..003d4da27 100644 --- a/packages/core/src/rum/instrumentation/interactionTracking/__tests__/DdBabelInteractionTracking.test.ts +++ b/packages/core/src/rum/instrumentation/interactionTracking/__tests__/DdBabelInteractionTracking.test.ts @@ -66,4 +66,32 @@ describe('DdBabelInteractionTracking.wrapRumAction', () => { expect(func).toHaveBeenCalledWith('arg1', 'arg2'); expect(result).toBe('result'); }); + + it('should forward the first handler argument as actionContext to DdRum.addAction', () => { + const mockAddAction = jest.fn().mockResolvedValue(undefined); + DdBabelInteractionTracking.config = { trackInteractions: true }; + DdBabelInteractionTracking.attachRumInstance({ + addAction: mockAddAction + } as any); + + const func = jest.fn(); + const wrapped = DdBabelInteractionTracking.wrapRumAction( + func, + RumActionType.TAP, + mockTargetObject + ); + + const event = { + nativeEvent: { target: 42, locationX: 10, locationY: 20 } + }; + wrapped(event); + + expect(mockAddAction).toHaveBeenCalledWith( + RumActionType.TAP, + expect.any(String), + expect.anything(), + expect.any(Number), + event + ); + }); }); diff --git a/packages/core/src/specs/NativeDdRum.ts b/packages/core/src/specs/NativeDdRum.ts index 9c0c459b3..dbe417f44 100644 --- a/packages/core/src/specs/NativeDdRum.ts +++ b/packages/core/src/specs/NativeDdRum.ts @@ -68,12 +68,14 @@ export interface Spec extends TurboModule { * Add a RUM Action. * @param type: The action type (tap, scroll, swipe, back, custom). * @param name: The action name. + * @param touch: The native touch data for tap actions, or null for other action types. * @param context: The additional context to send. * @param timestampMs: The timestamp when the action occurred (in milliseconds). If not provided, current timestamp will be used. */ addAction( type: string, name: string, + touch: Object | null, context: Object, timestampMs: number ): Promise; diff --git a/packages/react-native-babel-plugin/src/actions/rum/tap.ts b/packages/react-native-babel-plugin/src/actions/rum/tap.ts index 6e609ea92..1aa7e5665 100644 --- a/packages/react-native-babel-plugin/src/actions/rum/tap.ts +++ b/packages/react-native-babel-plugin/src/actions/rum/tap.ts @@ -60,7 +60,7 @@ export function handleTapAction( : { fName: null, fNode: null }; const handlerArgs = - isArrowFunc && expression?.params + isArrowFunc && expression?.params && expression.params.length > 0 ? t.arrayExpression( getArgumentsFromParams(t, state, expression.params).callArgs ) @@ -108,7 +108,9 @@ export function handleTapAction( if (handler && handler?.mode === 'delayed') { returnExpression = expression; } else { - returnExpression = isArrowFunc + const isArrowFuncWithParams = + isArrowFunc && expression.params.length > 0; + returnExpression = isArrowFuncWithParams ? t.callExpression( expression, getArgumentsFromParams(t, state, expression.params).callArgs @@ -460,7 +462,10 @@ function getActionWrapperFunction( argsObject: Babel.types.ObjectExpression, mode?: string ) { - const params = expressionParams || [t.restElement(t.identifier('args'))]; + const params = + expressionParams && expressionParams.length > 0 + ? expressionParams + : [t.restElement(t.identifier('args'))]; const { wrapperParams, diff --git a/packages/react-native-babel-plugin/test/plugin.test.ts b/packages/react-native-babel-plugin/test/plugin.test.ts index 2dc934804..cc579842f 100644 --- a/packages/react-native-babel-plugin/test/plugin.test.ts +++ b/packages/react-native-babel-plugin/test/plugin.test.ts @@ -118,7 +118,7 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { value: username, onChangeText: setUsername, style: styles.input, - onFocus: () => { + onFocus: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => { console.log('test'); }, "TAP", { @@ -129,11 +129,11 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { "getContent": () => { return __ddExtractText(_jsx(_Fragment, {}), []); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "TextInput" - })();else return (() => { + })(...args);else return (() => { console.log('test'); - })(); + })?.(...args); } });" `); @@ -159,7 +159,7 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { value: username, onChangeText: setUsername, style: styles.input, - onFocus: () => { + onFocus: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => {}, "TAP", { "options": { "useContent": true, @@ -168,9 +168,9 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { "getContent": () => { return __ddExtractText(_jsx(_Fragment, {}), []); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "TextInput" - })();else return (() => {})(); + })(...args);else return (() => {})?.(...args); } });" `); @@ -237,7 +237,7 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } import { TextInput } from 'react-native'; /*#__PURE__*/React.createElement(TextInput, _extends({}, props, { - onFocus: () => { + onFocus: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => console.log('Focused'), "TAP", { "options": { "useContent": true, @@ -246,9 +246,9 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { "getContent": () => { return __ddExtractText(_jsx(_Fragment, {}), []); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "TextInput" - })();else return (() => console.log('Focused'))(); + })(...args);else return (() => console.log('Focused'))?.(...args); } }));" `); @@ -346,7 +346,7 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } import { TextInput } from './TextInput'; /*#__PURE__*/React.createElement(TextInput, _extends({}, props, { - onFocus: () => { + onFocus: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => console.log('Focused'), "TAP", { "options": { "useContent": true, @@ -355,9 +355,9 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { "getContent": () => { return __ddExtractText(_jsx(_Fragment, {}), []); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "TextInput" - })();else return (() => console.log('Focused'))(); + })(...args);else return (() => console.log('Focused'))?.(...args); } }));" `); @@ -440,7 +440,7 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } import { TextInput } from './TextInput'; /*#__PURE__*/React.createElement(TextInput, _extends({}, props, { - onFocus: () => { + onFocus: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => console.log('Focused'), "TAP", { "options": { "useContent": true, @@ -449,9 +449,9 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { "getContent": () => { return __ddExtractText(_jsx(_Fragment, {}), []); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "TextInput" - })();else return (() => console.log('Focused'))(); + })(...args);else return (() => console.log('Focused'))?.(...args); } }));" `); @@ -1019,7 +1019,7 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { }) { return /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Pressable, { color: "red", - onPress: () => { + onPress: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => onPress(item.id), "TAP", { "options": { "useContent": true, @@ -1028,9 +1028,9 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { "getContent": () => { return __ddExtractText(_jsx(_Fragment, {}), []); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "Pressable" - })();else return (() => onPress(item.id))(); + })(...args);else return (() => onPress(item.id))?.(...args); } })); }" @@ -1075,7 +1075,7 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { } }, /*#__PURE__*/React.createElement(Button, { title: "Press Me", - onPress: () => { + onPress: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => this.handlePress(), "TAP", { "options": { "useContent": true, @@ -1084,9 +1084,9 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { "getContent": () => { return __ddExtractText(_jsx(_Fragment, {}), ["Press Me"]); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "Button" - })();else return (() => this.handlePress())(); + })(...args);else return (() => this.handlePress())?.(...args); } })); } @@ -1315,7 +1315,7 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )', }, [a, b]); return /*#__PURE__*/React.createElement(Pressable, { color: "red", - onPress: () => { + onPress: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => handler('Test'), "TAP", { "options": { "useContent": true, @@ -1324,9 +1324,9 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )', "getContent": () => { return __ddExtractText(_jsx(_Fragment, {}), []); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "Pressable" - })();else return (() => handler('Test'))(); + })(...args);else return (() => handler('Test'))?.(...args); } }); }" @@ -1356,7 +1356,7 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )', import { useCallback } from 'react'; import { Pressable } from 'react-native'; function MyComponent() { - const handler = useCallback(() => { + const handler = useCallback((...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => { console.log('Testing '); }, "TAP", { @@ -1369,7 +1369,7 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )', }, "handlerArgs": [], "componentName": "Pressable" - })();else return (() => { + })(...args);else return (() => { console.log('Testing '); })?.(); }, []); @@ -1406,7 +1406,7 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )', import { useCallback } from 'react'; import { View, Pressable } from 'react-native'; function MyComponent() { - const handler = useCallback(() => { + const handler = useCallback((...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => { console.log('Testing '); }, "TAP", { @@ -1419,7 +1419,7 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )', }, "handlerArgs": [], "componentName": "Pressable" - })();else return (() => { + })(...args);else return (() => { console.log('Testing '); })?.(); }, []); @@ -1659,7 +1659,7 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )', console.log('Testing'); }; function MyComponent() { - const handler = React.useMemo(() => { + const handler = React.useMemo((...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(funcN, "TAP", { "options": { "useContent": true, @@ -1670,7 +1670,7 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )', }, "handlerArgs": [], "componentName": "Pressable" - })();else return funcN?.(); + })(...args);else return funcN?.(); }, []); return /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Pressable, { color: "red", @@ -1825,7 +1825,7 @@ describe('Babel plugin: conditional expressions in getContent children', () => { function App() { const visible = true; return /*#__PURE__*/React.createElement(Pressable, { - onPress: () => { + onPress: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => {}, "TAP", { "options": { "useContent": true, @@ -1840,9 +1840,9 @@ describe('Babel plugin: conditional expressions in getContent children', () => { }) }), []); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "Pressable" - })();else return (() => {})(); + })(...args);else return (() => {})?.(...args); } }, visible ? /*#__PURE__*/React.createElement(Text, null, "Visible") : /*#__PURE__*/React.createElement(Text, null, "Invisible")); }" @@ -1872,7 +1872,7 @@ describe('Babel plugin: conditional expressions in getContent children', () => { function App() { const visible = true; return /*#__PURE__*/React.createElement(Pressable, { - onPress: () => { + onPress: (...args) => { if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => {}, "TAP", { "options": { "useContent": true, @@ -1885,9 +1885,9 @@ describe('Babel plugin: conditional expressions in getContent children', () => { }) }), []); }, - "handlerArgs": [], + "handlerArgs": [...args], "componentName": "Pressable" - })();else return (() => {})(); + })(...args);else return (() => {})?.(...args); } }, visible && /*#__PURE__*/React.createElement(Text, null, "Hello")); }" diff --git a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec index d354b40c0..1878b1e52 100644 --- a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec +++ b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogSessionReplay', '3.8.2' + s.dependency 'DatadogSessionReplay', '3.10.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt b/packages/react-native-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt index cc2fd64dc..7f7e6e0ec 100644 --- a/packages/react-native-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +++ b/packages/react-native-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt @@ -31,13 +31,17 @@ class DdSessionReplayImplementation( * @param privacySettings Defines the way visual elements should be masked. * @param customEndpoint Custom server url for sending replay data. * @param startRecordingImmediately Whether the recording should start immediately when the feature is enabled. + * @param enableHeatmaps Enables heatmap identifier computation. + * Currently unused on Android; reserved for heatmap support. */ @SuppressLint("VisibleForTests") + @Suppress("LongParameterList", "UnusedParameter") fun enable( replaySampleRate: Double, customEndpoint: String, privacySettings: SessionReplayPrivacySettings, startRecordingImmediately: Boolean, + enableHeatmaps: Boolean, promise: Promise ) { val sdkCore = Datadog.getInstance() as FeatureSdkCore diff --git a/packages/react-native-session-replay/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt b/packages/react-native-session-replay/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt index 0cd14ab7b..c2c5fe297 100644 --- a/packages/react-native-session-replay/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +++ b/packages/react-native-session-replay/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt @@ -29,7 +29,10 @@ class DdSessionReplay( * @param touchPrivacyLevel Defines the way user touches should be masked. * @param textAndInputPrivacyLevel Defines the way text and input should be masked. * @param startRecordingImmediately Whether the recording should start immediately when the feature is enabled. + * @param enableHeatmaps Enables heatmap identifier computation. + * Currently unused on Android; reserved for heatmap support. */ + @Suppress("LongParameterList") @ReactMethod override fun enable( replaySampleRate: Double, @@ -38,6 +41,7 @@ class DdSessionReplay( touchPrivacyLevel: String, textAndInputPrivacyLevel: String, startRecordingImmediately: Boolean, + enableHeatmaps: Boolean, promise: Promise ) { implementation.enable( @@ -49,6 +53,7 @@ class DdSessionReplay( textAndInputPrivacyLevel = textAndInputPrivacyLevel ), startRecordingImmediately, + enableHeatmaps, promise ) } diff --git a/packages/react-native-session-replay/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt b/packages/react-native-session-replay/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt index e8269a6e2..436f3289f 100644 --- a/packages/react-native-session-replay/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +++ b/packages/react-native-session-replay/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt @@ -30,7 +30,10 @@ class DdSessionReplay( * @param touchPrivacyLevel Defines the way user touches should be masked. * @param textAndInputPrivacyLevel Defines the way text and input should be masked. * @param startRecordingImmediately Whether the recording should start immediately when the feature is enabled. + * @param enableHeatmaps Enables heatmap identifier computation. + * Currently unused on Android; reserved for heatmap support. */ + @Suppress("LongParameterList") @ReactMethod fun enable( replaySampleRate: Double, @@ -39,6 +42,7 @@ class DdSessionReplay( touchPrivacyLevel: String, textAndInputPrivacyLevel: String, startRecordingImmediately: Boolean, + enableHeatmaps: Boolean, promise: Promise ) { implementation.enable( @@ -50,6 +54,7 @@ class DdSessionReplay( textAndInputPrivacyLevel = textAndInputPrivacyLevel ), startRecordingImmediately, + enableHeatmaps, promise ) } diff --git a/packages/react-native-session-replay/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt b/packages/react-native-session-replay/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt index 353c40635..b313ac1ed 100644 --- a/packages/react-native-session-replay/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt +++ b/packages/react-native-session-replay/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt @@ -131,6 +131,7 @@ internal class DdSessionReplayImplementationTest { customEndpoint, SessionReplayPrivacySettings(imagePrivacy, touchPrivacy, textAndInputPrivacy), startRecordingImmediately, + false, mockPromise ) @@ -165,6 +166,7 @@ internal class DdSessionReplayImplementationTest { textAndInputPrivacyLevel = textAndInputPrivacy ), startRecordingImmediately, + false, mockPromise ) diff --git a/packages/react-native-session-replay/ios/Sources/DdSessionReplay.mm b/packages/react-native-session-replay/ios/Sources/DdSessionReplay.mm index 942c37450..c020819e3 100644 --- a/packages/react-native-session-replay/ios/Sources/DdSessionReplay.mm +++ b/packages/react-native-session-replay/ios/Sources/DdSessionReplay.mm @@ -23,6 +23,7 @@ @implementation DdSessionReplay withTouchPrivacyLevel:(NSString*)touchPrivacyLevel withTextAndInputPrivacyLevel:(NSString*)textAndInputPrivacyLevel withStartRecordingImmediately:(BOOL)startRecordingImmediately + withEnableHeatmaps:(BOOL)enableHeatmaps withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) { @@ -32,6 +33,7 @@ @implementation DdSessionReplay touchPrivacyLevel:touchPrivacyLevel textAndInputPrivacyLevel:textAndInputPrivacyLevel startRecordingImmediately:startRecordingImmediately + enableHeatmaps:enableHeatmaps resolve:resolve reject:reject]; } @@ -73,6 +75,7 @@ - (void)enable:(double)replaySampleRate touchPrivacyLevel:(NSString *)touchPrivacyLevel textAndInputPrivacyLevel:(NSString *)textAndInputPrivacyLevel startRecordingImmediately:(BOOL)startRecordingImmediately + enableHeatmaps:(BOOL)enableHeatmaps resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [self.ddSessionReplayImplementation enableWithReplaySampleRate:replaySampleRate @@ -81,6 +84,7 @@ - (void)enable:(double)replaySampleRate touchPrivacyLevel:touchPrivacyLevel textAndInputPrivacyLevel:textAndInputPrivacyLevel startRecordingImmediately:startRecordingImmediately + enableHeatmaps:enableHeatmaps resolve:resolve reject:reject]; } diff --git a/packages/react-native-session-replay/ios/Sources/DdSessionReplayImplementation.swift b/packages/react-native-session-replay/ios/Sources/DdSessionReplayImplementation.swift index 0fc9c6637..402c744ce 100644 --- a/packages/react-native-session-replay/ios/Sources/DdSessionReplayImplementation.swift +++ b/packages/react-native-session-replay/ios/Sources/DdSessionReplayImplementation.swift @@ -50,6 +50,7 @@ public class DdSessionReplayImplementation: NSObject { touchPrivacyLevel: NSString, textAndInputPrivacyLevel: NSString, startRecordingImmediately: Bool, + enableHeatmaps: Bool, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock ) -> Void { @@ -66,7 +67,11 @@ public class DdSessionReplayImplementation: NSObject { startRecordingImmediately: startRecordingImmediately, customEndpoint: customEndpointURL ) - + + if enableHeatmaps { + sessionReplayConfiguration.featureFlags[.heatmaps] = true + } + var svgMap: [String: SVGData] = [:] if let bundle = Bundle.ddSessionReplayResources, diff --git a/packages/react-native-session-replay/ios/Tests/DdSessionReplayTests.swift b/packages/react-native-session-replay/ios/Tests/DdSessionReplayTests.swift index 067a11df0..5d26344c0 100644 --- a/packages/react-native-session-replay/ios/Tests/DdSessionReplayTests.swift +++ b/packages/react-native-session-replay/ios/Tests/DdSessionReplayTests.swift @@ -70,6 +70,7 @@ internal class DdSessionReplayTests: XCTestCase { touchPrivacyLevel: NSString(string: touchPrivacyLevel), textAndInputPrivacyLevel: NSString(string: textAndInputPrivacyLevel), startRecordingImmediately: true, + enableHeatmaps: false, resolve: mockResolve, reject: mockReject) @@ -79,10 +80,11 @@ internal class DdSessionReplayTests: XCTestCase { imagePrivacyLevel: imagePrivacy, touchPrivacyLevel: touchPrivacy, textAndInputPrivacyLevel: textAndInputPrivacy, - startRecordingImmediately: true + startRecordingImmediately: true, + enableHeatmaps: false )) } - + func testEnablesSessionReplayWithBadPrivacyLevels() { let sessionReplayMock = MockSessionReplay() let uiManagerMock = MockUIManager() @@ -99,19 +101,21 @@ internal class DdSessionReplayTests: XCTestCase { touchPrivacyLevel: "BAD_VALUE", textAndInputPrivacyLevel: "BAD_VALUE", startRecordingImmediately: true, + enableHeatmaps: false, resolve: mockResolve, reject: mockReject) - + XCTAssertEqual(sessionReplayMock.calledMethods.first, .enable( replaySampleRate: 100.0, customEndpoint: nil, imagePrivacyLevel: .maskAll, touchPrivacyLevel: .hide, textAndInputPrivacyLevel: .maskAll, - startRecordingImmediately: true + startRecordingImmediately: true, + enableHeatmaps: false )) } - + func testEnablesSessionReplayWithCustomEndpoint() { let sessionReplayMock = MockSessionReplay() let uiManagerMock = MockUIManager() @@ -140,16 +144,18 @@ internal class DdSessionReplayTests: XCTestCase { touchPrivacyLevel: NSString(string: touchPrivacyLevel), textAndInputPrivacyLevel: NSString(string: textAndInputPrivacyLevel), startRecordingImmediately: true, + enableHeatmaps: false, resolve: mockResolve, reject: mockReject) - + XCTAssertEqual(sessionReplayMock.calledMethods.first, .enable( replaySampleRate: 100.0, customEndpoint: URL(string: "https://session-replay.example.com/api/v2/replay"), imagePrivacyLevel: imagePrivacy, touchPrivacyLevel: touchPrivacy, textAndInputPrivacyLevel: textAndInputPrivacy, - startRecordingImmediately: true + startRecordingImmediately: true, + enableHeatmaps: false )) } } @@ -162,7 +168,8 @@ private class MockSessionReplay: SessionReplayProtocol { imagePrivacyLevel: ImagePrivacyLevel, touchPrivacyLevel: TouchPrivacyLevel, textAndInputPrivacyLevel: TextAndInputPrivacyLevel, - startRecordingImmediately: Bool + startRecordingImmediately: Bool, + enableHeatmaps: Bool ) case startRecording case stopRecording @@ -178,7 +185,8 @@ private class MockSessionReplay: SessionReplayProtocol { imagePrivacyLevel: configuration.imagePrivacyLevel, touchPrivacyLevel: configuration.touchPrivacyLevel, textAndInputPrivacyLevel: configuration.textAndInputPrivacyLevel, - startRecordingImmediately: configuration.startRecordingImmediately + startRecordingImmediately: configuration.startRecordingImmediately, + enableHeatmaps: configuration.featureFlags[.heatmaps] ?? false ) ) } diff --git a/packages/react-native-session-replay/ios/Tests/RCTTextViewRecorderTests.swift b/packages/react-native-session-replay/ios/Tests/RCTTextViewRecorderTests.swift index 7cbc466a6..176a24f3b 100644 --- a/packages/react-native-session-replay/ios/Tests/RCTTextViewRecorderTests.swift +++ b/packages/react-native-session-replay/ios/Tests/RCTTextViewRecorderTests.swift @@ -40,12 +40,14 @@ internal class RCTTextViewRecorderTests: XCTestCase { sessionID: "session_id", viewID: "view_id", viewServerTimeOffset: nil, + viewPath: nil, date: Date(), telemetry: NoOpTelemetry() ), coordinateSpace: UIView(), ids: .init(), webViewCache: .init(), + heatmapCache: .init(), clip: .zero ) @@ -161,12 +163,14 @@ internal class RCTTextViewRecorderTests: XCTestCase { sessionID: "session_id", viewID: "view_id", viewServerTimeOffset: nil, + viewPath: nil, date: Date(), telemetry: NoOpTelemetry() ), coordinateSpace: UIView(), ids: .init(), webViewCache: .init(), + heatmapCache: .init(), clip: .zero ) let reactTag = NSNumber(value: 44) diff --git a/packages/react-native-session-replay/src/SessionReplay.ts b/packages/react-native-session-replay/src/SessionReplay.ts index 516b8ffb4..8e9567f8f 100644 --- a/packages/react-native-session-replay/src/SessionReplay.ts +++ b/packages/react-native-session-replay/src/SessionReplay.ts @@ -95,12 +95,21 @@ export interface SessionReplayConfiguration { * Default: `true`. */ startRecordingImmediately?: boolean; + + /** + * Enables heatmap identifier computation (preview feature). + * When `true`, Session Replay computes a stable identifier for each + * view, enabling heatmap visualization for tap actions. + * Default: `false`. + */ + enableHeatmaps?: boolean; } type InternalBaseSessionReplayConfiguration = { replaySampleRate: number; customEndpoint: string; startRecordingImmediately: boolean; + enableHeatmaps: boolean; }; type InternalPrivacySessionReplayConfiguration = { @@ -118,7 +127,8 @@ const DEFAULTS: InternalSessionReplayConfiguration = { imagePrivacyLevel: ImagePrivacyLevel.MASK_ALL, touchPrivacyLevel: TouchPrivacyLevel.HIDE, textAndInputPrivacyLevel: TextAndInputPrivacyLevel.MASK_ALL, - startRecordingImmediately: true + startRecordingImmediately: true, + enableHeatmaps: false }; export class SessionReplayWrapper { @@ -135,7 +145,8 @@ export class SessionReplayWrapper { const { replaySampleRate, customEndpoint, - startRecordingImmediately + startRecordingImmediately, + enableHeatmaps } = configuration; const baseConfig: InternalBaseSessionReplayConfiguration = { @@ -150,7 +161,11 @@ export class SessionReplayWrapper { startRecordingImmediately: startRecordingImmediately !== undefined ? startRecordingImmediately - : DEFAULTS.startRecordingImmediately + : DEFAULTS.startRecordingImmediately, + enableHeatmaps: + enableHeatmaps !== undefined + ? enableHeatmaps + : DEFAULTS.enableHeatmaps }; const privacyConfig: InternalPrivacySessionReplayConfiguration = { @@ -177,7 +192,8 @@ export class SessionReplayWrapper { imagePrivacyLevel, touchPrivacyLevel, textAndInputPrivacyLevel, - startRecordingImmediately + startRecordingImmediately, + enableHeatmaps } = this.buildConfiguration(configuration); return this.nativeSessionReplay.enable( @@ -186,7 +202,8 @@ export class SessionReplayWrapper { imagePrivacyLevel, touchPrivacyLevel, textAndInputPrivacyLevel, - startRecordingImmediately + startRecordingImmediately, + enableHeatmaps ); }; diff --git a/packages/react-native-session-replay/src/__tests__/SessionReplay.test.ts b/packages/react-native-session-replay/src/__tests__/SessionReplay.test.ts index 43f449cf7..1ae0b1fa4 100644 --- a/packages/react-native-session-replay/src/__tests__/SessionReplay.test.ts +++ b/packages/react-native-session-replay/src/__tests__/SessionReplay.test.ts @@ -36,7 +36,8 @@ describe('SessionReplay', () => { 'MASK_ALL', 'HIDE', 'MASK_ALL', - true + true, + false ); }); @@ -52,7 +53,8 @@ describe('SessionReplay', () => { 'MASK_ALL', 'HIDE', 'MASK_ALL', - true + true, + false ); }); @@ -80,7 +82,8 @@ describe('SessionReplay', () => { image, touch, textAndInput, - true + true, + false ); } }); @@ -97,7 +100,8 @@ describe('SessionReplay', () => { 'MASK_ALL', 'HIDE', 'MASK_ALL', - true + true, + false ); }); }); diff --git a/packages/react-native-session-replay/src/nativeModulesTypes.ts b/packages/react-native-session-replay/src/nativeModulesTypes.ts index 86e7d4668..0ca28ac97 100644 --- a/packages/react-native-session-replay/src/nativeModulesTypes.ts +++ b/packages/react-native-session-replay/src/nativeModulesTypes.ts @@ -39,7 +39,8 @@ export interface NativeSessionReplayType extends NativeDdSessionReplay { imagePrivacyLevel: ImagePrivacyLevel, touchPrivacyLevel: TouchPrivacyLevel, textAndInputPrivacyLevel: TextAndInputPrivacyLevel, - startRecordingImmediately: boolean + startRecordingImmediately: boolean, + enableHeatmaps: boolean ): Promise; /** diff --git a/packages/react-native-session-replay/src/specs/NativeDdSessionReplay.ts b/packages/react-native-session-replay/src/specs/NativeDdSessionReplay.ts index d057b3703..30b91499e 100644 --- a/packages/react-native-session-replay/src/specs/NativeDdSessionReplay.ts +++ b/packages/react-native-session-replay/src/specs/NativeDdSessionReplay.ts @@ -31,7 +31,8 @@ export interface Spec extends TurboModule { imagePrivacyLevel: string, touchPrivacyLevel: string, textAndInputPrivacyLevel: string, - startRecordingImmediately: boolean + startRecordingImmediately: boolean, + enableHeatmaps: boolean ): Promise; /** diff --git a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec index 788349cda..e854a6cfc 100644 --- a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec +++ b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec @@ -23,8 +23,8 @@ Pod::Spec.new do |s| end # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogWebViewTracking', '3.8.2' - s.dependency 'DatadogInternal', '3.8.2' + s.dependency 'DatadogWebViewTracking', '3.10.0' + s.dependency 'DatadogInternal', '3.10.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec|