@datadog/mobile-react-native-session-replay 2.6.1 → 2.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +5 -5
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupport.kt +5 -29
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ShadowNodeWrapper.kt +2 -2
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactEditTextMapper.kt +3 -26
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactTextMapper.kt +3 -25
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/text/FabricTextViewUtils.kt +74 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/text/LegacyTextViewUtils.kt +118 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/{ReactTextPropertiesResolver.kt → utils/text/TextViewUtils.kt} +60 -96
- package/android/src/rn75/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt +1 -2
- package/android/src/rn75/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +1 -3
- package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt +1 -1
- package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +1 -3
- package/android/src/rnlegacy/kotlin/com.datadog.reactnative.sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +2 -7
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupportTest.kt +3 -17
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/{ReactTextPropertiesResolverTest.kt → utils/text/TextViewUtilsTest.kt} +171 -38
- package/ios/Sources/DdSessionReplay.mm +4 -4
- package/ios/Sources/DdSessionReplayImplementation.swift +13 -3
- package/ios/Sources/RCTFabricWrapper.h +13 -0
- package/ios/Sources/RCTFabricWrapper.mm +120 -0
- package/ios/Sources/RCTTextPropertiesWrapper.h +23 -0
- package/ios/Sources/RCTTextPropertiesWrapper.mm +28 -0
- package/ios/Sources/RCTTextViewRecorder.swift +69 -49
- package/ios/Sources/RCTVersion.h +8 -0
- package/package.json +5 -3
- package/scripts/set-ios-rn-version.js +47 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/NoopTextPropertiesResolver.kt +0 -22
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/TextViewUtils.kt +0 -40
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/utils/TextViewUtilsTest.kt +0 -109
|
@@ -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
|
|
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
|
|
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)
|
|
88
|
+
- (void)startRecording:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
89
89
|
[self.ddSessionReplayImplementation startRecordingWithResolve:resolve reject:reject];
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
- (void)
|
|
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(
|
|
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([
|
|
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
|
|
@@ -17,27 +17,11 @@ internal class RCTTextViewRecorder: SessionReplayNodeRecorder {
|
|
|
17
17
|
internal var identifier = UUID()
|
|
18
18
|
|
|
19
19
|
internal let uiManager: RCTUIManager
|
|
20
|
+
internal let fabricWrapper: RCTFabricWrapper
|
|
20
21
|
|
|
21
|
-
internal init(uiManager: RCTUIManager) {
|
|
22
|
+
internal init(uiManager: RCTUIManager, fabricWrapper: RCTFabricWrapper) {
|
|
22
23
|
self.uiManager = uiManager
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
internal func extractTextFromSubViews(
|
|
26
|
-
subviews: [RCTShadowView]?
|
|
27
|
-
) -> String? {
|
|
28
|
-
if let subviews = subviews {
|
|
29
|
-
return subviews.compactMap { subview in
|
|
30
|
-
if let sub = subview as? RCTRawTextShadowView {
|
|
31
|
-
return sub.text
|
|
32
|
-
}
|
|
33
|
-
if let sub = subview as? RCTVirtualTextShadowView {
|
|
34
|
-
// We recursively get all subviews for nested Text components
|
|
35
|
-
return extractTextFromSubViews(subviews: sub.reactSubviews())
|
|
36
|
-
}
|
|
37
|
-
return nil
|
|
38
|
-
}.joined()
|
|
39
|
-
}
|
|
40
|
-
return nil
|
|
24
|
+
self.fabricWrapper = fabricWrapper
|
|
41
25
|
}
|
|
42
26
|
|
|
43
27
|
public func semantics(
|
|
@@ -45,6 +29,48 @@ internal class RCTTextViewRecorder: SessionReplayNodeRecorder {
|
|
|
45
29
|
with attributes: SessionReplayViewAttributes,
|
|
46
30
|
in context: SessionReplayViewTreeRecordingContext
|
|
47
31
|
) -> SessionReplayNodeSemantics? {
|
|
32
|
+
guard
|
|
33
|
+
let textProperties = fabricWrapper.tryToExtractTextProperties(from: view) ?? tryToExtractTextProperties(view: view)
|
|
34
|
+
else {
|
|
35
|
+
return view is RCTTextView ? SessionReplayInvisibleElement.constant : nil
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let builder = RCTTextViewWireframesBuilder(
|
|
39
|
+
wireframeID: context.ids.nodeID(view: view, nodeRecorder: self),
|
|
40
|
+
attributes: attributes,
|
|
41
|
+
text: textProperties.text,
|
|
42
|
+
textAlignment: textProperties.alignment,
|
|
43
|
+
textColor: textProperties.foregroundColor,
|
|
44
|
+
textObfuscator: textObfuscator(context),
|
|
45
|
+
fontSize: textProperties.fontSize,
|
|
46
|
+
contentRect: textProperties.contentRect
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return SessionReplaySpecificElement(subtreeStrategy: .ignore, nodes: [
|
|
50
|
+
SessionReplayNode(viewAttributes: attributes, wireframesBuilder: builder)
|
|
51
|
+
])
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
internal func tryToExtractTextFromSubViews(
|
|
55
|
+
subviews: [RCTShadowView]?
|
|
56
|
+
) -> String? {
|
|
57
|
+
guard let subviews = subviews else {
|
|
58
|
+
return nil
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return subviews.compactMap { subview in
|
|
62
|
+
if let sub = subview as? RCTRawTextShadowView {
|
|
63
|
+
return sub.text
|
|
64
|
+
}
|
|
65
|
+
if let sub = subview as? RCTVirtualTextShadowView {
|
|
66
|
+
// We recursively get all subviews for nested Text components
|
|
67
|
+
return tryToExtractTextFromSubViews(subviews: sub.reactSubviews())
|
|
68
|
+
}
|
|
69
|
+
return nil
|
|
70
|
+
}.joined()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private func tryToExtractTextProperties(view: UIView) -> RCTTextPropertiesWrapper? {
|
|
48
74
|
guard let textView = view as? RCTTextView else {
|
|
49
75
|
return nil
|
|
50
76
|
}
|
|
@@ -56,41 +82,35 @@ internal class RCTTextViewRecorder: SessionReplayNodeRecorder {
|
|
|
56
82
|
shadowView = uiManager.shadowView(forReactTag: tag) as? RCTTextShadowView
|
|
57
83
|
}
|
|
58
84
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
subviews: shadow.reactSubviews()
|
|
63
|
-
)
|
|
85
|
+
guard let shadow = shadowView else {
|
|
86
|
+
return nil
|
|
87
|
+
}
|
|
64
88
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
textColor: shadow.textAttributes.foregroundColor?.cgColor,
|
|
71
|
-
textObfuscator: textObfuscator(context),
|
|
72
|
-
fontSize: shadow.textAttributes.fontSize,
|
|
73
|
-
contentRect: shadow.contentFrame
|
|
74
|
-
)
|
|
75
|
-
let node = SessionReplayNode(viewAttributes: attributes, wireframesBuilder: builder)
|
|
76
|
-
return SessionReplaySpecificElement(subtreeStrategy: .ignore, nodes: [node])
|
|
89
|
+
let textProperties = RCTTextPropertiesWrapper()
|
|
90
|
+
|
|
91
|
+
// TODO: RUM-2173 check performance is ok
|
|
92
|
+
if let text = tryToExtractTextFromSubViews(subviews: shadow.reactSubviews()) {
|
|
93
|
+
textProperties.text = text
|
|
77
94
|
}
|
|
78
|
-
return SessionReplayInvisibleElement.constant
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
95
|
|
|
82
|
-
|
|
83
|
-
|
|
96
|
+
if let foregroundColor = shadow.textAttributes.foregroundColor {
|
|
97
|
+
textProperties.foregroundColor = foregroundColor
|
|
98
|
+
}
|
|
84
99
|
|
|
85
|
-
|
|
86
|
-
|
|
100
|
+
textProperties.alignment = shadow.textAttributes.alignment
|
|
101
|
+
textProperties.fontSize = shadow.textAttributes.fontSize
|
|
102
|
+
textProperties.contentRect = shadow.contentFrame
|
|
103
|
+
|
|
104
|
+
return textProperties
|
|
105
|
+
}
|
|
106
|
+
}
|
|
87
107
|
|
|
88
108
|
internal struct RCTTextViewWireframesBuilder: SessionReplayNodeWireframesBuilder {
|
|
89
109
|
let wireframeID: WireframeID
|
|
90
110
|
let attributes: SessionReplayViewAttributes
|
|
91
|
-
let text: String
|
|
111
|
+
let text: String
|
|
92
112
|
var textAlignment: NSTextAlignment
|
|
93
|
-
let textColor:
|
|
113
|
+
let textColor: UIColor
|
|
94
114
|
let textObfuscator: SessionReplayTextObfuscating
|
|
95
115
|
let fontSize: CGFloat
|
|
96
116
|
let contentRect: CGRect
|
|
@@ -140,12 +160,12 @@ internal struct RCTTextViewWireframesBuilder: SessionReplayNodeWireframesBuilder
|
|
|
140
160
|
id: wireframeID,
|
|
141
161
|
frame: relativeIntersectedRect,
|
|
142
162
|
clip: attributes.clip,
|
|
143
|
-
text: textObfuscator.mask(text: text
|
|
163
|
+
text: textObfuscator.mask(text: text),
|
|
144
164
|
textFrame: textFrame,
|
|
145
|
-
// Text alignment is top for all RCTTextView components.
|
|
165
|
+
// Text alignment is top for all RCTTextView and RCTParagraphComponentView components.
|
|
146
166
|
textAlignment: .init(systemTextAlignment: textAlignment, vertical: .top),
|
|
147
|
-
textColor: textColor
|
|
148
|
-
fontOverride: SessionReplayWireframesBuilder.FontOverride(size: fontSize.isNaN ?
|
|
167
|
+
textColor: textColor.cgColor,
|
|
168
|
+
fontOverride: SessionReplayWireframesBuilder.FontOverride(size: fontSize.isNaN ? RCTTextPropertiesDefaultFontSize : fontSize),
|
|
149
169
|
borderColor: attributes.layerBorderColor,
|
|
150
170
|
borderWidth: attributes.layerBorderWidth,
|
|
151
171
|
backgroundColor: attributes.backgroundColor,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datadog/mobile-react-native-session-replay",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.3",
|
|
4
4
|
"description": "A client-side React Native module to enable session replay with Datadog",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"datadog",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"files": [
|
|
20
20
|
"src/**",
|
|
21
21
|
"lib/**",
|
|
22
|
+
"scripts/**",
|
|
22
23
|
"android/build.gradle",
|
|
23
24
|
"android/detekt.yml",
|
|
24
25
|
"android/gradle.properties",
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
"scripts": {
|
|
41
42
|
"test": "jest",
|
|
42
43
|
"lint": "eslint .",
|
|
43
|
-
"prepare": "rm -rf lib && yarn bob build"
|
|
44
|
+
"prepare": "rm -rf lib && yarn bob build",
|
|
45
|
+
"postinstall": "node scripts/set-ios-rn-version.js"
|
|
44
46
|
},
|
|
45
47
|
"peerDependencies": {
|
|
46
48
|
"react": ">=16.13.1",
|
|
@@ -88,5 +90,5 @@
|
|
|
88
90
|
"javaPackageName": "com.datadog.reactnative.sessionreplay"
|
|
89
91
|
}
|
|
90
92
|
},
|
|
91
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "1870ee0f82b7773c107dce3b16766526401e7a56"
|
|
92
94
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
function getReactNativeVersion() {
|
|
11
|
+
try {
|
|
12
|
+
// eslint-disable-next-line global-require
|
|
13
|
+
return require('react-native/package.json').version;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
'Failed to find React Native. Ensure it is installed in your project.'
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const rnVersion = getReactNativeVersion();
|
|
22
|
+
|
|
23
|
+
const outputDir = path.resolve(__dirname, '../ios/Sources');
|
|
24
|
+
const outputFile = path.join(outputDir, 'RCTVersion.h');
|
|
25
|
+
|
|
26
|
+
if (!fs.existsSync(outputDir)) {
|
|
27
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const [major, minor, patch] = rnVersion.split('.').map(Number);
|
|
31
|
+
|
|
32
|
+
const headerContent = `#ifndef RCTVersion_h
|
|
33
|
+
#define RCTVersion_h
|
|
34
|
+
|
|
35
|
+
#define RCT_VERSION_MAJOR ${major || 0}
|
|
36
|
+
#define RCT_VERSION_MINOR ${minor || 0}
|
|
37
|
+
#define RCT_VERSION_PATCH ${patch || 0}
|
|
38
|
+
|
|
39
|
+
#endif /* RCTVersion_h */
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
fs.writeFileSync(outputFile, headerContent, 'utf8');
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`Failed to write RCTVersion.h: ${error.message}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/NoopTextPropertiesResolver.kt
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
*
|
|
3
|
-
* * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
|
|
4
|
-
* * This product includes software developed at Datadog (https://www.datadoghq.com/).
|
|
5
|
-
* * Copyright 2016-Present Datadog, Inc.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
package com.datadog.reactnative.sessionreplay
|
|
10
|
-
|
|
11
|
-
import android.widget.TextView
|
|
12
|
-
import com.datadog.android.sessionreplay.model.MobileSegment
|
|
13
|
-
|
|
14
|
-
internal class NoopTextPropertiesResolver: TextPropertiesResolver {
|
|
15
|
-
override fun addReactNativeProperties(
|
|
16
|
-
originalWireframe: MobileSegment.Wireframe.TextWireframe,
|
|
17
|
-
view: TextView,
|
|
18
|
-
pixelDensity: Float
|
|
19
|
-
): MobileSegment.Wireframe.TextWireframe {
|
|
20
|
-
return originalWireframe
|
|
21
|
-
}
|
|
22
|
-
}
|
package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/TextViewUtils.kt
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
*
|
|
3
|
-
* * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
|
|
4
|
-
* * This product includes software developed at Datadog (https://www.datadoghq.com/).
|
|
5
|
-
* * Copyright 2016-Present Datadog, Inc.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
package com.datadog.reactnative.sessionreplay.utils
|
|
10
|
-
|
|
11
|
-
import android.widget.TextView
|
|
12
|
-
import com.datadog.android.sessionreplay.model.MobileSegment
|
|
13
|
-
import com.datadog.android.sessionreplay.recorder.MappingContext
|
|
14
|
-
import com.datadog.reactnative.sessionreplay.TextPropertiesResolver
|
|
15
|
-
|
|
16
|
-
internal class TextViewUtils {
|
|
17
|
-
internal fun mapTextViewToWireframes(
|
|
18
|
-
wireframes: List<MobileSegment.Wireframe>,
|
|
19
|
-
view: TextView,
|
|
20
|
-
mappingContext: MappingContext,
|
|
21
|
-
reactTextPropertiesResolver: TextPropertiesResolver
|
|
22
|
-
): List<MobileSegment.Wireframe> {
|
|
23
|
-
val result = mutableListOf<MobileSegment.Wireframe>()
|
|
24
|
-
val pixelDensity = mappingContext.systemInformation.screenDensity
|
|
25
|
-
|
|
26
|
-
for (originalWireframe in wireframes) {
|
|
27
|
-
if (originalWireframe !is MobileSegment.Wireframe.TextWireframe) {
|
|
28
|
-
result.add(originalWireframe)
|
|
29
|
-
} else {
|
|
30
|
-
result.add(reactTextPropertiesResolver.addReactNativeProperties(
|
|
31
|
-
originalWireframe = originalWireframe,
|
|
32
|
-
view = view,
|
|
33
|
-
pixelDensity = pixelDensity,
|
|
34
|
-
))
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return result
|
|
39
|
-
}
|
|
40
|
-
}
|