@datadog/mobile-react-native-session-replay 2.6.0 → 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +52 -1
  3. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +5 -5
  4. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupport.kt +5 -29
  5. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ShadowNodeWrapper.kt +2 -2
  6. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactEditTextMapper.kt +3 -26
  7. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactTextMapper.kt +3 -25
  8. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/text/FabricTextViewUtils.kt +74 -0
  9. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/text/LegacyTextViewUtils.kt +118 -0
  10. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/{ReactTextPropertiesResolver.kt → utils/text/TextViewUtils.kt} +60 -96
  11. package/android/src/rn75/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt +1 -2
  12. package/android/src/rn75/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +1 -3
  13. package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt +1 -1
  14. package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +1 -3
  15. package/android/src/rnlegacy/kotlin/com.datadog.reactnative.sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +2 -7
  16. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupportTest.kt +3 -17
  17. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/{ReactTextPropertiesResolverTest.kt → utils/text/TextViewUtilsTest.kt} +171 -38
  18. package/ios/Sources/DdSessionReplay.mm +4 -4
  19. package/ios/Sources/DdSessionReplayImplementation.swift +13 -3
  20. package/ios/Sources/RCTFabricWrapper.h +13 -0
  21. package/ios/Sources/RCTFabricWrapper.mm +120 -0
  22. package/ios/Sources/RCTTextPropertiesWrapper.h +23 -0
  23. package/ios/Sources/RCTTextPropertiesWrapper.mm +28 -0
  24. package/ios/Sources/RCTTextViewRecorder.swift +69 -49
  25. package/ios/Sources/RCTVersion.h +8 -0
  26. package/lib/commonjs/SessionReplay.js +1 -1
  27. package/lib/commonjs/SessionReplay.js.map +1 -1
  28. package/lib/module/SessionReplay.js +1 -1
  29. package/lib/module/SessionReplay.js.map +1 -1
  30. package/package.json +6 -3
  31. package/scripts/set-ios-rn-version.js +47 -0
  32. package/src/SessionReplay.ts +1 -1
  33. package/src/__tests__/SessionReplay.test.ts +1 -1
  34. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/NoopTextPropertiesResolver.kt +0 -22
  35. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/TextViewUtils.kt +0 -40
  36. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/utils/TextViewUtilsTest.kt +0 -109
@@ -4,20 +4,30 @@
4
4
  * Copyright 2016-Present Datadog, Inc.
5
5
  */
6
6
 
7
- package com.datadog.reactnative.sessionreplay
7
+ package com.datadog.reactnative.sessionreplay.utils.text
8
8
 
9
+ import android.content.res.Resources
10
+ import android.graphics.Typeface
11
+ import android.text.Spannable
12
+ import android.text.style.ForegroundColorSpan
13
+ import android.util.DisplayMetrics
9
14
  import android.widget.TextView
15
+ import com.datadog.android.api.InternalLogger
10
16
  import com.datadog.android.sessionreplay.model.MobileSegment
11
- import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.COLOR_FIELD_NAME
12
- import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.FONT_FAMILY_FIELD_NAME
13
- import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.IS_COLOR_SET_FIELD_NAME
14
- import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.MONOSPACE_FAMILY_NAME
15
- import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.TEXT_ATTRIBUTES_FIELD_NAME
17
+ import com.datadog.android.sessionreplay.recorder.MappingContext
18
+ import com.datadog.android.sessionreplay.recorder.SystemInformation
19
+ import com.datadog.reactnative.sessionreplay.ShadowNodeWrapper
16
20
  import com.datadog.reactnative.sessionreplay.ShadowNodeWrapper.Companion.UI_IMPLEMENTATION_FIELD_NAME
17
21
  import com.datadog.reactnative.sessionreplay.utils.DrawableUtils
18
22
  import com.datadog.reactnative.sessionreplay.utils.ReflectionUtils
19
23
  import com.datadog.reactnative.sessionreplay.utils.formatAsRgba
24
+ import com.datadog.reactnative.sessionreplay.utils.text.TextViewUtils.Companion.COLOR_FIELD_NAME
25
+ import com.datadog.reactnative.sessionreplay.utils.text.TextViewUtils.Companion.FONT_FAMILY_FIELD_NAME
26
+ import com.datadog.reactnative.sessionreplay.utils.text.TextViewUtils.Companion.IS_COLOR_SET_FIELD_NAME
27
+ import com.datadog.reactnative.sessionreplay.utils.text.TextViewUtils.Companion.MONOSPACE_FAMILY_NAME
28
+ import com.datadog.reactnative.sessionreplay.utils.text.TextViewUtils.Companion.TEXT_ATTRIBUTES_FIELD_NAME
20
29
  import com.datadog.reactnative.tools.unit.forge.ForgeConfigurator
30
+ import com.facebook.react.bridge.NativeModule
21
31
  import com.facebook.react.bridge.ReactContext
22
32
  import com.facebook.react.uimanager.ReactShadowNode
23
33
  import com.facebook.react.uimanager.UIImplementation
@@ -34,7 +44,11 @@ import org.junit.jupiter.api.BeforeEach
34
44
  import org.junit.jupiter.api.Test
35
45
  import org.junit.jupiter.api.extension.ExtendWith
36
46
  import org.junit.jupiter.api.extension.Extensions
47
+ import org.mockito.ArgumentMatchers.anyInt
37
48
  import org.mockito.Mock
49
+ import org.mockito.Mockito.doReturn
50
+ import org.mockito.Mockito.mock
51
+ import org.mockito.Mockito.spy
38
52
  import org.mockito.junit.jupiter.MockitoExtension
39
53
  import org.mockito.junit.jupiter.MockitoSettings
40
54
  import org.mockito.kotlin.any
@@ -48,9 +62,7 @@ import org.mockito.quality.Strictness
48
62
  )
49
63
  @MockitoSettings(strictness = Strictness.LENIENT)
50
64
  @ForgeConfiguration(ForgeConfigurator::class)
51
- internal class ReactTextPropertiesResolverTest {
52
- private lateinit var testedResolver: ReactTextPropertiesResolver
53
-
65
+ internal class TextViewUtilsTest {
54
66
  @Mock
55
67
  lateinit var mockReactContext: ReactContext
56
68
 
@@ -84,10 +96,44 @@ internal class ReactTextPropertiesResolverTest {
84
96
  @Mock
85
97
  private lateinit var mockShadowNode: ReactShadowNode<out ReactShadowNode<*>>
86
98
 
99
+ @Mock
100
+ private lateinit var mockLogger: InternalLogger
101
+
102
+ @Mock
103
+ private lateinit var mockMappingContext: MappingContext
104
+
105
+ @Mock
106
+ private lateinit var mockSystemInformation: SystemInformation
107
+
108
+ @Mock
109
+ private lateinit var mockResources: Resources
110
+
111
+ @Mock
112
+ private lateinit var mockDisplayMetrics: DisplayMetrics
113
+
114
+ @Mock
115
+ private lateinit var testedUtils: LegacyTextViewUtils
116
+
117
+ @Mock
118
+ private lateinit var fabricTestedUtils: FabricTextViewUtils
119
+
87
120
  @BeforeEach
88
121
  fun `set up`(forge: Forge) {
122
+ whenever(mockResources.displayMetrics).thenReturn(mockDisplayMetrics)
123
+ whenever(mockTextView.resources).thenReturn(mockResources)
124
+ whenever(mockSystemInformation.screenDensity).thenReturn(0f)
125
+ whenever(mockMappingContext.systemInformation).thenReturn(mockSystemInformation)
126
+ whenever(mockTextView.text).thenReturn(forge.aString())
127
+ whenever(mockTextView.typeface).thenReturn(Typeface.SANS_SERIF)
128
+
129
+ whenever(mockReactContext.getNativeModule(UIManagerModule::class.java))
130
+ .thenReturn(mockUiManagerModule)
131
+
89
132
  whenever(
90
- mockReflectionUtils.getDeclaredField(mockUiManagerModule, UI_IMPLEMENTATION_FIELD_NAME)
133
+ mockReflectionUtils.getDeclaredField(
134
+ eq(mockUiManagerModule),
135
+ eq(UI_IMPLEMENTATION_FIELD_NAME)
136
+ )
91
137
  ).thenReturn(mockUiImplementation)
92
138
 
93
139
  whenever(
@@ -101,12 +147,64 @@ internal class ReactTextPropertiesResolverTest {
101
147
  }
102
148
  whenever(mockReactContext.hasActiveReactInstance()).thenReturn(true)
103
149
 
104
- testedResolver = ReactTextPropertiesResolver(
105
- reactContext = mockReactContext,
106
- uiManagerModule = mockUiManagerModule,
107
- drawableUtils = mockDrawableUtils,
108
- reflectionUtils = mockReflectionUtils
109
- )
150
+ val realUtils =
151
+ LegacyTextViewUtils(
152
+ mockReactContext,
153
+ mockLogger,
154
+ mockReflectionUtils,
155
+ mockDrawableUtils
156
+ )
157
+
158
+ val realFabricUtils =
159
+ FabricTextViewUtils(
160
+ mockReactContext,
161
+ mockLogger,
162
+ mockDrawableUtils
163
+ )
164
+
165
+ testedUtils = spy(realUtils)
166
+ fabricTestedUtils = spy(realFabricUtils)
167
+ }
168
+
169
+ @Test
170
+ fun `M return wireframe W map() { even if not TextWireframeType }`(
171
+ @Mock mockImageWireframe: MobileSegment.Wireframe.ImageWireframe
172
+ ) {
173
+ // When
174
+ val result =
175
+ testedUtils.mapTextViewToWireframes(
176
+ wireframes = listOf(mockImageWireframe),
177
+ view = mockTextView,
178
+ mappingContext = mockMappingContext
179
+ )
180
+
181
+ // Then
182
+ assertThat(result).contains(mockImageWireframe)
183
+ }
184
+
185
+ @Test
186
+ fun `M return textWireframe W map()`(
187
+ @Mock mockTextWireframe: MobileSegment.Wireframe.TextWireframe
188
+ ) {
189
+ // Given
190
+ doReturn(mockTextWireframe)
191
+ .whenever(testedUtils)
192
+ .addReactNativeProperties(
193
+ originalWireframe = eq(mockTextWireframe),
194
+ view = eq(mockTextView),
195
+ pixelDensity = eq(0f)
196
+ )
197
+
198
+ // When
199
+ val result =
200
+ testedUtils.mapTextViewToWireframes(
201
+ wireframes = listOf(mockTextWireframe),
202
+ view = mockTextView,
203
+ mappingContext = mockMappingContext
204
+ )[0] as MobileSegment.Wireframe.TextWireframe
205
+
206
+ // Then
207
+ assertThat(result).isEqualTo(mockTextWireframe)
110
208
  }
111
209
 
112
210
  // region addReactNativeProperties
@@ -118,16 +216,15 @@ internal class ReactTextPropertiesResolverTest {
118
216
  whenever(mockUiImplementation.resolveShadowNode(any())).thenReturn(null)
119
217
 
120
218
  // When
121
- val result = testedResolver.addReactNativeProperties(mockWireframe, mockTextView, 0f)
219
+ val result = testedUtils.addReactNativeProperties(mockWireframe, mockTextView, 0f)
122
220
 
123
221
  // Then
124
222
  assertThat(result).isEqualTo(mockWireframe)
125
223
  }
126
224
 
127
225
  @Test
128
- fun `M add drawable properties W addReactNativeProperties() { has reactBackgroundDrawable }`(
129
- forge: Forge
130
- ) {
226
+ fun `M add drawable properties W addReactNativeProperties() { has reactBackgroundDrawable }`
227
+ (forge: Forge) {
131
228
  // Given
132
229
  val pixelDensity = 0f
133
230
  val fakeBorderRadius = forge.aPositiveFloat()
@@ -150,18 +247,20 @@ internal class ReactTextPropertiesResolverTest {
150
247
  backgroundColor = formatAsRgba(fakeBorderColor),
151
248
  opacity = 0f,
152
249
  cornerRadius = fakeBorderRadius.toLong()
153
- ) to MobileSegment.ShapeBorder(
154
- color = formatAsRgba(fakeBorderColor),
155
- width = fakeBorderWidth.toLong()
156
- )
250
+ ) to
251
+ MobileSegment.ShapeBorder(
252
+ color = formatAsRgba(fakeBorderColor),
253
+ width = fakeBorderWidth.toLong()
254
+ )
157
255
  )
158
256
 
159
257
  // When
160
- val result = testedResolver.addReactNativeProperties(
161
- fakeWireframe,
162
- mockTextView,
163
- pixelDensity
164
- )
258
+ val result =
259
+ testedUtils.addReactNativeProperties(
260
+ fakeWireframe,
261
+ mockTextView,
262
+ pixelDensity
263
+ )
165
264
 
166
265
  // Then
167
266
  assertThat(result.shapeStyle?.cornerRadius).isEqualTo(fakeBorderRadius.toLong())
@@ -178,8 +277,9 @@ internal class ReactTextPropertiesResolverTest {
178
277
  whenever(mockTextView.background).thenReturn(null)
179
278
 
180
279
  // When
181
- val result = testedResolver
182
- .addReactNativeProperties(fakeWireframe, mockTextView, 0f)
280
+ val result =
281
+ testedUtils
282
+ .addReactNativeProperties(fakeWireframe, mockTextView, 0f)
183
283
 
184
284
  // Then
185
285
  assertThat(result.textStyle.family)
@@ -194,7 +294,7 @@ internal class ReactTextPropertiesResolverTest {
194
294
  .thenReturn(null)
195
295
 
196
296
  // When
197
- val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
297
+ val result = testedUtils.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
198
298
 
199
299
  // Then
200
300
  assertThat(result.textStyle.family).isEqualTo(fakeWireframe.textStyle.family)
@@ -212,7 +312,7 @@ internal class ReactTextPropertiesResolverTest {
212
312
  whenever(mockTextAttributes.effectiveFontSize).thenReturn(fakeTextSize)
213
313
 
214
314
  // When
215
- val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
315
+ val result = testedUtils.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
216
316
 
217
317
  // Then
218
318
  assertThat(result.textStyle.size).isEqualTo(fakeTextSize.toLong())
@@ -228,7 +328,7 @@ internal class ReactTextPropertiesResolverTest {
228
328
  .thenReturn(null)
229
329
 
230
330
  // When
231
- val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
331
+ val result = testedUtils.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
232
332
 
233
333
  // Then
234
334
  assertThat(result.textStyle.size).isEqualTo(fakeWireframe.textStyle.size)
@@ -246,7 +346,7 @@ internal class ReactTextPropertiesResolverTest {
246
346
  .thenReturn(fakeTextColor)
247
347
 
248
348
  // When
249
- val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
349
+ val result = testedUtils.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
250
350
 
251
351
  // Then
252
352
  assertThat(result.textStyle.color).isEqualTo(formatAsRgba(fakeTextColor))
@@ -264,7 +364,7 @@ internal class ReactTextPropertiesResolverTest {
264
364
  .thenReturn(fakeTextColor)
265
365
 
266
366
  // When
267
- val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
367
+ val result = testedUtils.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
268
368
 
269
369
  // Then
270
370
  assertThat(result.textStyle.color).isEqualTo("#000000FF")
@@ -280,7 +380,7 @@ internal class ReactTextPropertiesResolverTest {
280
380
  .thenReturn(null)
281
381
 
282
382
  // When
283
- val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
383
+ val result = testedUtils.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
284
384
 
285
385
  // Then
286
386
  assertThat(result.textStyle.color).isEqualTo(fakeWireframe.textStyle.color)
@@ -294,11 +394,44 @@ internal class ReactTextPropertiesResolverTest {
294
394
  .thenReturn(MONOSPACE_FAMILY_NAME)
295
395
 
296
396
  // When
297
- val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
397
+ val result = testedUtils.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
298
398
 
299
399
  // Then
300
400
  assertThat(result.textStyle.family).isNotEqualTo(MONOSPACE_FAMILY_NAME)
301
401
  }
302
402
 
403
+ @Test
404
+ fun `M return fabric textStyle (color) W addReactNativeProperties`() {
405
+ val mockForegroundColorSpan = mock(ForegroundColorSpan::class.java)
406
+ whenever(mockForegroundColorSpan.foregroundColor).thenReturn(-1)
407
+
408
+ val spannable = mock(Spannable::class.java)
409
+ doReturn(spannable).whenever(fabricTestedUtils).getFieldFromView(any(), any())
410
+
411
+ whenever(spannable.getSpans(anyInt(), anyInt(), eq(ForegroundColorSpan::class.java)))
412
+ .thenReturn(
413
+ arrayOf(mockForegroundColorSpan)
414
+ )
415
+
416
+ val result = fabricTestedUtils.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
417
+ assertThat(result.textStyle.color).isEqualTo("#ffffffff")
418
+ }
419
+
420
+ // endregion
421
+
422
+ // region getUiManagerModule
423
+ @Test
424
+ fun `M return null W getUiManagerModule() { cannot get uiManagerModule }`() {
425
+ // Given
426
+ whenever(mockReactContext.getNativeModule(any<Class<NativeModule>>()))
427
+ .thenThrow(IllegalStateException())
428
+
429
+ // When
430
+ val uiManagerModule = testedUtils.getUiManagerModule()
431
+
432
+ // Then
433
+ assertThat(uiManagerModule).isNull()
434
+ }
435
+
303
436
  // endregion
304
437
  }
@@ -38,12 +38,12 @@ RCT_REMAP_METHOD(enable, withEnableReplaySampleRate:(double)replaySampleRate
38
38
 
39
39
  RCT_EXPORT_METHOD(startRecording:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
40
40
  {
41
- [self startRecordingWithResolver:resolve reject:reject];
41
+ [self startRecording:resolve reject:reject];
42
42
  }
43
43
 
44
44
  RCT_EXPORT_METHOD(stopRecording:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
45
45
  {
46
- [self stopRecordingWithResolver:resolve reject:reject];
46
+ [self stopRecording:resolve reject:reject];
47
47
  }
48
48
 
49
49
  // Thanks to this guard, we won't compile this code when we build for the old architecture.
@@ -85,11 +85,11 @@ RCT_EXPORT_METHOD(stopRecording:(RCTPromiseResolveBlock)resolve withRejecter:(RC
85
85
  reject:reject];
86
86
  }
87
87
 
88
- - (void)startRecordingWithResolver:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
88
+ - (void)startRecording:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
89
89
  [self.ddSessionReplayImplementation startRecordingWithResolve:resolve reject:reject];
90
90
  }
91
91
 
92
- - (void)stopRecordingWithResolver:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
92
+ - (void)stopRecording:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
93
93
  [self.ddSessionReplayImplementation stopRecordingWithResolve:resolve reject:reject];
94
94
  }
95
95
 
@@ -15,17 +15,24 @@ public class DdSessionReplayImplementation: NSObject {
15
15
  private lazy var sessionReplay: SessionReplayProtocol = sessionReplayProvider()
16
16
  private let sessionReplayProvider: () -> SessionReplayProtocol
17
17
  private let uiManager: RCTUIManager
18
+ private let fabricWrapper: RCTFabricWrapper
18
19
 
19
- internal init(sessionReplayProvider: @escaping () -> SessionReplayProtocol, uiManager: RCTUIManager) {
20
+ internal init(
21
+ sessionReplayProvider: @escaping () -> SessionReplayProtocol,
22
+ uiManager: RCTUIManager,
23
+ fabricWrapper: RCTFabricWrapper
24
+ ) {
20
25
  self.sessionReplayProvider = sessionReplayProvider
21
26
  self.uiManager = uiManager
27
+ self.fabricWrapper = fabricWrapper
22
28
  }
23
29
 
24
30
  @objc
25
31
  public convenience init(bridge: RCTBridge) {
26
32
  self.init(
27
33
  sessionReplayProvider: { NativeSessionReplay() },
28
- uiManager: bridge.uiManager
34
+ uiManager: bridge.uiManager,
35
+ fabricWrapper: RCTFabricWrapper()
29
36
  )
30
37
  }
31
38
 
@@ -44,6 +51,7 @@ public class DdSessionReplayImplementation: NSObject {
44
51
  if (customEndpoint != "") {
45
52
  customEndpointURL = URL(string: "\(customEndpoint)/api/v2/replay" as String)
46
53
  }
54
+
47
55
  var sessionReplayConfiguration = SessionReplay.Configuration(
48
56
  replaySampleRate: Float(replaySampleRate),
49
57
  textAndInputPrivacyLevel: convertTextAndInputPrivacy(textAndInputPrivacyLevel),
@@ -53,7 +61,9 @@ public class DdSessionReplayImplementation: NSObject {
53
61
  customEndpoint: customEndpointURL
54
62
  )
55
63
 
56
- sessionReplayConfiguration.setAdditionalNodeRecorders([RCTTextViewRecorder(uiManager: self.uiManager)])
64
+ sessionReplayConfiguration.setAdditionalNodeRecorders([
65
+ RCTTextViewRecorder(uiManager: uiManager, fabricWrapper: fabricWrapper)
66
+ ])
57
67
 
58
68
  if let core = DatadogSDKWrapper.shared.getCoreInstance() {
59
69
  sessionReplay.enable(
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+ #import <Foundation/Foundation.h>
7
+ #import "RCTTextPropertiesWrapper.h"
8
+
9
+ @interface RCTFabricWrapper : NSObject
10
+
11
+ - (nullable RCTTextPropertiesWrapper*)tryToExtractTextPropertiesFromView:(UIView* _Nonnull)view;
12
+
13
+ @end
@@ -0,0 +1,120 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+
7
+ #import "RCTFabricWrapper.h"
8
+
9
+ #if RCT_NEW_ARCH_ENABLED
10
+ #import "RCTVersion.h"
11
+
12
+ #import <React-RCTFabric/React/RCTParagraphComponentView.h>
13
+ #import <React-RCTFabric/React/RCTConversions.h>
14
+
15
+ #if RCT_VERSION_MINOR > 74
16
+ #import <React-FabricComponents/react/renderer/components/text/ParagraphProps.h>
17
+ #else
18
+ #import <React-Fabric/react/renderer/components/text/ParagraphProps.h>
19
+ #endif
20
+
21
+
22
+ namespace rct = facebook::react;
23
+ #endif
24
+
25
+ @implementation RCTFabricWrapper
26
+ /**
27
+ * Extracts the text properties from the given UIView when the view is of type RCTParagraphComponentView, returns nil otherwise.
28
+ */
29
+ - (nullable RCTTextPropertiesWrapper*)tryToExtractTextPropertiesFromView:(UIView *)view {
30
+ #if RCT_NEW_ARCH_ENABLED
31
+ if (![view isKindOfClass:[RCTParagraphComponentView class]]) {
32
+ return nil;
33
+ }
34
+
35
+ // Cast view to RCTParagraphComponentView
36
+ RCTParagraphComponentView* paragraphComponentView = (RCTParagraphComponentView *)view;
37
+ if (paragraphComponentView == nil) {
38
+ return nil;
39
+ }
40
+
41
+ // Retrieve ParagraphProps from shared pointer
42
+ const rct::ParagraphProps* props = (rct::ParagraphProps*)paragraphComponentView.props.get();
43
+ if (props == nil) {
44
+ return nil;
45
+ }
46
+
47
+ // Extract Attributes
48
+ RCTTextPropertiesWrapper* textPropertiesWrapper = [[RCTTextPropertiesWrapper alloc] init];
49
+ textPropertiesWrapper.text = [RCTFabricWrapper getTextFromView:paragraphComponentView];
50
+ textPropertiesWrapper.contentRect = paragraphComponentView.bounds;
51
+
52
+ rct::TextAttributes textAttributes = props->textAttributes;
53
+ textPropertiesWrapper.alignment = [RCTFabricWrapper getAlignmentFromAttributes:textAttributes];
54
+ textPropertiesWrapper.foregroundColor = [RCTFabricWrapper getForegroundColorFromAttributes:textAttributes];
55
+ textPropertiesWrapper.fontSize = [RCTFabricWrapper getFontSizeFromAttributes:textAttributes];
56
+
57
+ return textPropertiesWrapper;
58
+ #else
59
+ return nil;
60
+ #endif
61
+ }
62
+
63
+ #if RCT_NEW_ARCH_ENABLED
64
+ + (NSString* _Nonnull)getTextFromView:(RCTParagraphComponentView*)view {
65
+ if (view == nil || view.attributedText == nil) {
66
+ return RCTTextPropertiesDefaultText;
67
+ }
68
+
69
+ return view.attributedText.string;
70
+ }
71
+
72
+ + (NSTextAlignment)getAlignmentFromAttributes:(rct::TextAttributes)textAttributes {
73
+ const rct::TextAlignment alignment = textAttributes.alignment.has_value() ?
74
+ textAttributes.alignment.value() :
75
+ rct::TextAlignment::Natural;
76
+
77
+ switch (alignment) {
78
+ case rct::TextAlignment::Natural:
79
+ return NSTextAlignmentNatural;
80
+
81
+ case rct::TextAlignment::Left:
82
+ return NSTextAlignmentLeft;
83
+
84
+ case rct::TextAlignment::Center:
85
+ return NSTextAlignmentCenter;
86
+
87
+ case rct::TextAlignment::Right:
88
+ return NSTextAlignmentRight;
89
+
90
+ case rct::TextAlignment::Justified:
91
+ return NSTextAlignmentJustified;
92
+
93
+ default:
94
+ return RCTTextPropertiesDefaultAlignment;
95
+ }
96
+ }
97
+
98
+ + (UIColor* _Nonnull)getForegroundColorFromAttributes:(rct::TextAttributes)textAttributes {
99
+ @try {
100
+ #if RCT_VERSION_MINOR > 73
101
+ rct::Color color = *textAttributes.foregroundColor;
102
+ UIColor* uiColor = (__bridge UIColor*)color.getUIColor().get();
103
+ if (uiColor != nil) {
104
+ return uiColor;
105
+ }
106
+ #else
107
+ return RCTUIColorFromSharedColor(textAttributes.foregroundColor);
108
+ #endif
109
+ } @catch (NSException *exception) {}
110
+
111
+ return RCTTextPropertiesDefaultForegroundColor;
112
+ }
113
+
114
+ + (CGFloat)getFontSizeFromAttributes:(rct::TextAttributes)textAttributes {
115
+ // Float is just an alias for CGFloat, but this could change in the future.
116
+ _Static_assert(sizeof(rct::Float) == sizeof(CGFloat), "Float and CGFloat are expected to have the same size.");
117
+ return isnan(textAttributes.fontSize) ? RCTTextPropertiesDefaultFontSize : (CGFloat)textAttributes.fontSize;
118
+ }
119
+ #endif
120
+ @end
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+
7
+ @interface RCTTextPropertiesWrapper : NSObject
8
+
9
+ extern NSString* const RCTTextPropertiesDefaultText;
10
+ extern NSTextAlignment const RCTTextPropertiesDefaultAlignment;
11
+ extern UIColor* const RCTTextPropertiesDefaultForegroundColor;
12
+ extern CGFloat const RCTTextPropertiesDefaultFontSize;
13
+ extern CGRect const RCTTextPropertiesDefaultContentRect;
14
+
15
+ @property (nonatomic, strong, nonnull) NSString* text;
16
+ @property (nonatomic, assign) NSTextAlignment alignment;
17
+ @property (nonatomic, strong, nonnull) UIColor* foregroundColor;
18
+ @property (nonatomic, assign) CGFloat fontSize;
19
+ @property (nonatomic, assign) CGRect contentRect;
20
+
21
+ - (instancetype _Nonnull) init;
22
+
23
+ @end
@@ -0,0 +1,28 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+ #import "RCTTextPropertiesWrapper.h"
7
+
8
+ @implementation RCTTextPropertiesWrapper
9
+
10
+ NSString* const RCTTextPropertiesDefaultText = @"";
11
+ NSTextAlignment const RCTTextPropertiesDefaultAlignment = NSTextAlignmentNatural;
12
+ UIColor* const RCTTextPropertiesDefaultForegroundColor = [UIColor blackColor];
13
+ CGFloat const RCTTextPropertiesDefaultFontSize = 14.0;
14
+ CGRect const RCTTextPropertiesDefaultContentRect = CGRectZero;
15
+
16
+ - (instancetype)init {
17
+ self = [super init];
18
+ if (self) {
19
+ _text = RCTTextPropertiesDefaultText;
20
+ _alignment = RCTTextPropertiesDefaultAlignment;
21
+ _foregroundColor = RCTTextPropertiesDefaultForegroundColor;
22
+ _fontSize = RCTTextPropertiesDefaultFontSize;
23
+ _contentRect = RCTTextPropertiesDefaultContentRect;
24
+ }
25
+ return self;
26
+ }
27
+
28
+ @end