@multiplayer-app/session-recorder-react-native 0.0.1-beta.1 → 0.0.1-beta.10

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 (167) hide show
  1. package/SessionRecorderNative.podspec +29 -0
  2. package/android/build.gradle +32 -0
  3. package/app.plugin.js +42 -0
  4. package/copy-react-native-dist.sh +4 -10
  5. package/dist/components/MaskableComponent.d.ts +22 -0
  6. package/dist/components/MaskableComponent.js +1 -0
  7. package/dist/components/MaskableComponent.js.map +1 -0
  8. package/dist/components/MaskableTextInput.d.ts +14 -0
  9. package/dist/components/MaskableTextInput.js +1 -0
  10. package/dist/components/MaskableTextInput.js.map +1 -0
  11. package/dist/components/SessionRecorderWidget/FinalPopover.d.ts +11 -0
  12. package/dist/components/SessionRecorderWidget/FinalPopover.js +1 -0
  13. package/dist/components/SessionRecorderWidget/FinalPopover.js.map +1 -0
  14. package/dist/components/SessionRecorderWidget/FloatingButton.d.ts +8 -0
  15. package/dist/components/SessionRecorderWidget/FloatingButton.js +1 -0
  16. package/dist/components/SessionRecorderWidget/FloatingButton.js.map +1 -0
  17. package/dist/components/SessionRecorderWidget/InitialPopover.d.ts +13 -0
  18. package/dist/components/SessionRecorderWidget/InitialPopover.js +1 -0
  19. package/dist/components/SessionRecorderWidget/InitialPopover.js.map +1 -0
  20. package/dist/components/SessionRecorderWidget/ModalContainer.d.ts +8 -0
  21. package/dist/components/SessionRecorderWidget/ModalContainer.js +1 -0
  22. package/dist/components/SessionRecorderWidget/ModalContainer.js.map +1 -0
  23. package/dist/components/SessionRecorderWidget/ModalHeader.d.ts +6 -0
  24. package/dist/components/SessionRecorderWidget/ModalHeader.js +1 -0
  25. package/dist/components/SessionRecorderWidget/ModalHeader.js.map +1 -0
  26. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.d.ts +5 -0
  27. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js +1 -0
  28. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -0
  29. package/dist/components/SessionRecorderWidget/icons.d.ts +11 -0
  30. package/dist/components/SessionRecorderWidget/icons.js +1 -0
  31. package/dist/components/SessionRecorderWidget/icons.js.map +1 -0
  32. package/dist/components/SessionRecorderWidget/index.d.ts +2 -0
  33. package/dist/components/SessionRecorderWidget/index.js +1 -0
  34. package/dist/components/SessionRecorderWidget/index.js.map +1 -0
  35. package/dist/components/SessionRecorderWidget/styles.d.ts +145 -0
  36. package/dist/components/SessionRecorderWidget/styles.js +1 -0
  37. package/dist/components/SessionRecorderWidget/styles.js.map +1 -0
  38. package/dist/components/index.d.ts +2 -0
  39. package/dist/components/index.js +1 -1
  40. package/dist/components/index.js.map +1 -1
  41. package/dist/config/defaults.js +1 -1
  42. package/dist/config/defaults.js.map +1 -1
  43. package/dist/config/masking.js +1 -1
  44. package/dist/config/masking.js.map +1 -1
  45. package/dist/context/SessionRecorderContext.d.ts +5 -3
  46. package/dist/context/SessionRecorderContext.js +1 -1
  47. package/dist/context/SessionRecorderContext.js.map +1 -1
  48. package/dist/index.d.ts +0 -1
  49. package/dist/index.js +1 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/native/ScreenMasking.d.ts +21 -0
  52. package/dist/native/ScreenMasking.js +1 -0
  53. package/dist/native/ScreenMasking.js.map +1 -0
  54. package/dist/native/SessionRecorderNative.d.ts +21 -0
  55. package/dist/native/SessionRecorderNative.js +1 -0
  56. package/dist/native/SessionRecorderNative.js.map +1 -0
  57. package/dist/patch/xhr.js +1 -1
  58. package/dist/patch/xhr.js.map +1 -1
  59. package/dist/recorder/screenRecorder.d.ts +1 -0
  60. package/dist/recorder/screenRecorder.js +1 -1
  61. package/dist/recorder/screenRecorder.js.map +1 -1
  62. package/dist/recorder/screenshotManager.d.ts +10 -0
  63. package/dist/recorder/screenshotManager.js +1 -0
  64. package/dist/recorder/screenshotManager.js.map +1 -0
  65. package/dist/services/screenMaskingService.d.ts +39 -0
  66. package/dist/services/screenMaskingService.js +1 -0
  67. package/dist/services/screenMaskingService.js.map +1 -0
  68. package/dist/services/storage.service.d.ts +18 -2
  69. package/dist/services/storage.service.js +1 -1
  70. package/dist/services/storage.service.js.map +1 -1
  71. package/dist/session-recorder.d.ts +2 -1
  72. package/dist/session-recorder.js +1 -1
  73. package/dist/session-recorder.js.map +1 -1
  74. package/dist/types/session-recorder.d.ts +6 -0
  75. package/dist/types/session-recorder.js.map +1 -1
  76. package/dist/utils/componentRegistry.d.ts +64 -0
  77. package/dist/utils/componentRegistry.js +1 -0
  78. package/dist/utils/componentRegistry.js.map +1 -0
  79. package/dist/utils/nativeModuleTest.d.ts +8 -0
  80. package/dist/utils/nativeModuleTest.js +1 -0
  81. package/dist/utils/nativeModuleTest.js.map +1 -0
  82. package/dist/utils/platform.d.ts +3 -0
  83. package/dist/utils/reactNativeHierarchyExtractor.d.ts +38 -0
  84. package/dist/utils/reactNativeHierarchyExtractor.js +1 -0
  85. package/dist/utils/reactNativeHierarchyExtractor.js.map +1 -0
  86. package/dist/utils/screenshotMasker.d.ts +96 -0
  87. package/dist/utils/screenshotMasker.js +1 -0
  88. package/dist/utils/screenshotMasker.js.map +1 -0
  89. package/dist/utils/viewHierarchyTracker.d.ts +89 -0
  90. package/dist/utils/viewHierarchyTracker.js +1 -0
  91. package/dist/utils/viewHierarchyTracker.js.map +1 -0
  92. package/docs/NATIVE_MODULE_SETUP.md +177 -0
  93. package/ios/SessionRecorderNative.m +17 -0
  94. package/ios/SessionRecorderNative.podspec +26 -0
  95. package/ios/SessionRecorderNative.swift +205 -0
  96. package/package.json +22 -7
  97. package/plugin/package.json +20 -0
  98. package/plugin/src/index.js +42 -0
  99. package/react-native.config.js +15 -0
  100. package/RRWEB_INTEGRATION.md +0 -336
  101. package/VIEWSHOT_INTEGRATION_TEST.md +0 -123
  102. package/babel.config.js +0 -13
  103. package/dist/components/GestureCaptureWrapper.d.ts +0 -6
  104. package/dist/components/GestureCaptureWrapper.js +0 -1
  105. package/dist/components/GestureCaptureWrapper.js.map +0 -1
  106. package/dist/expo.d.ts +0 -7
  107. package/dist/expo.js +0 -1
  108. package/dist/expo.js.map +0 -1
  109. package/dist/otel/instrumentations/gestureInstrumentation.d.ts +0 -15
  110. package/dist/otel/instrumentations/gestureInstrumentation.js +0 -1
  111. package/dist/otel/instrumentations/gestureInstrumentation.js.map +0 -1
  112. package/dist/otel/instrumentations/reactNativeInstrumentation.d.ts +0 -8
  113. package/dist/otel/instrumentations/reactNativeInstrumentation.js +0 -1
  114. package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +0 -1
  115. package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +0 -13
  116. package/dist/otel/instrumentations/reactNavigationInstrumentation.js +0 -1
  117. package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +0 -1
  118. package/dist/recorder/gestureHandlerRecorder.d.ts +0 -19
  119. package/dist/recorder/gestureHandlerRecorder.js +0 -1
  120. package/dist/recorder/gestureHandlerRecorder.js.map +0 -1
  121. package/dist/types/rrweb.d.ts +0 -118
  122. package/dist/types/rrweb.js +0 -1
  123. package/dist/types/rrweb.js.map +0 -1
  124. package/docs/AUTO_METADATA_DETECTION.md +0 -108
  125. package/scripts/generate-app-metadata.js +0 -173
  126. package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +0 -86
  127. package/src/components/GestureCaptureWrapper/index.ts +0 -1
  128. package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +0 -72
  129. package/src/components/ScreenRecorderView/index.ts +0 -1
  130. package/src/components/index.ts +0 -1
  131. package/src/config/constants.ts +0 -60
  132. package/src/config/defaults.ts +0 -82
  133. package/src/config/index.ts +0 -6
  134. package/src/config/masking.ts +0 -27
  135. package/src/config/session-recorder.ts +0 -55
  136. package/src/config/validators.ts +0 -31
  137. package/src/context/SessionRecorderContext.tsx +0 -75
  138. package/src/expo.ts +0 -11
  139. package/src/index.ts +0 -17
  140. package/src/otel/helpers.ts +0 -275
  141. package/src/otel/index.ts +0 -138
  142. package/src/otel/instrumentations/index.ts +0 -115
  143. package/src/patch/index.ts +0 -1
  144. package/src/patch/xhr.ts +0 -142
  145. package/src/recorder/eventExporter.ts +0 -141
  146. package/src/recorder/gestureRecorder.ts +0 -498
  147. package/src/recorder/index.ts +0 -179
  148. package/src/recorder/navigationTracker.ts +0 -449
  149. package/src/recorder/screenRecorder.ts +0 -498
  150. package/src/services/api.service.ts +0 -203
  151. package/src/services/storage.service.ts +0 -158
  152. package/src/session-recorder.ts +0 -600
  153. package/src/types/expo.d.ts +0 -23
  154. package/src/types/index.ts +0 -28
  155. package/src/types/session-recorder.ts +0 -423
  156. package/src/types/session.ts +0 -65
  157. package/src/utils/app-metadata.ts +0 -31
  158. package/src/utils/index.ts +0 -8
  159. package/src/utils/logger.ts +0 -225
  160. package/src/utils/platform.ts +0 -384
  161. package/src/utils/request-utils.ts +0 -61
  162. package/src/utils/rrweb-events.ts +0 -309
  163. package/src/utils/session.ts +0 -18
  164. package/src/utils/time.ts +0 -17
  165. package/src/utils/type-utils.ts +0 -75
  166. package/src/version.ts +0 -1
  167. package/tsconfig.json +0 -24
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewHierarchyTracker.js","sourceRoot":"","sources":["../../src/utils/viewHierarchyTracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,177 @@
1
+ # Native Module Setup Guide
2
+
3
+ This guide explains how to properly set up the Session Recorder React Native library with native modules in both React Native and Expo projects.
4
+
5
+ ## Issues Fixed
6
+
7
+ ### 1. Missing iOS Native Files in NPM Package
8
+
9
+ **Problem**: The iOS native implementation files (`SessionRecorderNative.m` and `SessionRecorderNative.swift`) were not being included in the published npm package.
10
+
11
+ **Solution**: Updated `.npmignore` to exclude source files but include the compiled native modules.
12
+
13
+ ### 2. Auto-linking Configuration
14
+
15
+ **Problem**: The library wasn't properly configured for auto-linking in Expo projects.
16
+
17
+ **Solution**:
18
+
19
+ - Added `expo` configuration section to `package.json`
20
+ - Created an Expo plugin for automatic Podfile configuration
21
+ - Updated podspec with proper Expo compatibility settings
22
+
23
+ ### 3. Podspec Configuration
24
+
25
+ **Problem**: The podspec wasn't optimized for Expo compatibility.
26
+
27
+ **Solution**: Added proper header search paths and Expo-specific configurations.
28
+
29
+ ## Setup Instructions
30
+
31
+ ### For Expo Projects
32
+
33
+ 1. **Install the package**:
34
+
35
+ ```bash
36
+ npm install @multiplayer-app/session-recorder-react-native
37
+ ```
38
+
39
+ 2. **Add to app.json/app.config.js**:
40
+
41
+ ```json
42
+ {
43
+ "expo": {
44
+ "plugins": ["@multiplayer-app/session-recorder-react-native"]
45
+ }
46
+ }
47
+ ```
48
+
49
+ The plugin will automatically be detected via the `app.plugin.js` file in the package root.
50
+
51
+ 3. **Run prebuild** (if using development build):
52
+
53
+ ```bash
54
+ npx expo prebuild --clean
55
+ ```
56
+
57
+ 4. **Install iOS dependencies**:
58
+ ```bash
59
+ cd ios && pod install && cd ..
60
+ ```
61
+
62
+ ### For React Native CLI Projects
63
+
64
+ 1. **Install the package**:
65
+
66
+ ```bash
67
+ npm install @multiplayer-app/session-recorder-react-native
68
+ ```
69
+
70
+ 2. **iOS Setup**:
71
+
72
+ ```bash
73
+ cd ios && pod install && cd ..
74
+ ```
75
+
76
+ 3. **Android Setup**: No additional steps required (auto-linking handles it)
77
+
78
+ ## Troubleshooting
79
+
80
+ ### "Pod SessionRecorderNative files are missing"
81
+
82
+ This error occurs when the iOS native files aren't properly included in the package or auto-linking fails.
83
+
84
+ **Solutions**:
85
+
86
+ 1. **Check package installation**:
87
+
88
+ ```bash
89
+ ls node_modules/@multiplayer-app/session-recorder-react-native/ios/
90
+ ```
91
+
92
+ Should show: `SessionRecorderNative.m`, `SessionRecorderNative.podspec`, `SessionRecorderNative.swift`
93
+
94
+ 2. **Clear and reinstall**:
95
+
96
+ ```bash
97
+ rm -rf node_modules
98
+ npm install
99
+ cd ios && pod install --repo-update && cd ..
100
+ ```
101
+
102
+ 3. **For Expo projects, ensure plugin is configured**:
103
+ ```json
104
+ {
105
+ "expo": {
106
+ "plugins": ["@multiplayer-app/session-recorder-react-native"]
107
+ }
108
+ }
109
+ ```
110
+
111
+ ### Auto-linking Issues
112
+
113
+ If auto-linking doesn't work:
114
+
115
+ 1. **Check react-native.config.js** (should be automatically created):
116
+
117
+ ```javascript
118
+ module.exports = {
119
+ dependencies: {
120
+ '@multiplayer-app/session-recorder-react-native': {
121
+ platforms: {
122
+ android: {
123
+ sourceDir: '../android',
124
+ packageImportPath: 'import com.multiplayer.sessionrecorder.SessionRecorderPackage;'
125
+ },
126
+ ios: {
127
+ podspecPath: '../ios/SessionRecorderNative.podspec'
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ 2. **Manual linking** (if auto-linking fails):
136
+ - iOS: Add to Podfile manually
137
+ - Android: Add to MainApplication.java manually
138
+
139
+ ## File Structure
140
+
141
+ The package includes these native files:
142
+
143
+ ```
144
+ ios/
145
+ ├── SessionRecorderNative.m # Objective-C bridge
146
+ ├── SessionRecorderNative.podspec # CocoaPods specification
147
+ └── SessionRecorderNative.swift # Swift implementation
148
+
149
+ android/
150
+ ├── build.gradle # Android build configuration
151
+ └── src/main/java/com/multiplayer/sessionrecorder/
152
+ ├── SessionRecorderModule.kt # Main Android module
153
+ ├── SessionRecorderPackage.kt # Package registration
154
+ ├── ScreenMaskingModule.kt # Screen masking module
155
+ └── ScreenMaskingPackage.kt # Screen masking package
156
+ ```
157
+
158
+ ## Verification
159
+
160
+ To verify the setup is working:
161
+
162
+ 1. **Check iOS**:
163
+
164
+ ```bash
165
+ cd ios && pod list | grep SessionRecorderNative
166
+ ```
167
+
168
+ 2. **Check Android**: Look for the package in `android/app/src/main/java/`
169
+
170
+ 3. **Test in code**:
171
+
172
+ ```javascript
173
+ import { SessionRecorderNative } from '@multiplayer-app/session-recorder-react-native'
174
+
175
+ // This should not throw an error
176
+ console.log('Native module loaded:', SessionRecorderNative)
177
+ ```
@@ -0,0 +1,17 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface RCT_EXTERN_MODULE(SessionRecorderNative, NSObject)
4
+
5
+ RCT_EXTERN_METHOD(captureAndMask:(RCTPromiseResolveBlock)resolve
6
+ reject:(RCTPromiseRejectBlock)reject)
7
+
8
+ RCT_EXTERN_METHOD(captureAndMaskWithOptions:(NSDictionary *)options
9
+ resolve:(RCTPromiseResolveBlock)resolve
10
+ reject:(RCTPromiseRejectBlock)reject)
11
+
12
+ + (BOOL)requiresMainQueueSetup
13
+ {
14
+ return YES;
15
+ }
16
+
17
+ @end
@@ -0,0 +1,26 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "..", "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "SessionRecorderNative"
7
+ s.version = package["version"]
8
+ s.summary = "Native session recorder module for React Native"
9
+ s.description = "A native module that provides session recording with automatic masking of sensitive UI elements"
10
+ s.homepage = "https://github.com/multiplayer-app/multiplayer-session-recorder-javascript"
11
+ s.license = "MIT"
12
+ s.authors = { "Multiplayer Software, Inc." => "https://www.multiplayer.app" }
13
+ s.platforms = { :ios => "12.0" }
14
+ s.source = { :git => "https://github.com/multiplayer-app/multiplayer-session-recorder-javascript.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
17
+ s.requires_arc = true
18
+
19
+ s.dependency "React-Core"
20
+ s.dependency "React"
21
+
22
+ # Ensure proper linking for Expo
23
+ s.pod_target_xcconfig = {
24
+ "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/React-Core/React\""
25
+ }
26
+ end
@@ -0,0 +1,205 @@
1
+ import UIKit
2
+ import React
3
+
4
+ @objc(SessionRecorderNative)
5
+ class SessionRecorderNative: NSObject {
6
+
7
+ @objc func captureAndMask(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
8
+ DispatchQueue.main.async {
9
+ guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else {
10
+ reject("NO_WINDOW", "Unable to get key window", nil)
11
+ return
12
+ }
13
+
14
+ UIGraphicsBeginImageContextWithOptions(window.bounds.size, false, UIScreen.main.scale)
15
+ window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
16
+ let screenshot = UIGraphicsGetImageFromCurrentImageContext()
17
+ UIGraphicsEndImageContext()
18
+
19
+ guard let image = screenshot else {
20
+ reject("CAPTURE_FAILED", "Failed to capture screen", nil)
21
+ return
22
+ }
23
+
24
+ // Apply masking to sensitive elements
25
+ let maskedImage = self.applyMasking(to: image, in: window)
26
+
27
+ if let data = maskedImage.jpegData(compressionQuality: 0.5) {
28
+ let base64 = data.base64EncodedString()
29
+ resolve(base64)
30
+ } else {
31
+ reject("ENCODING_FAILED", "Failed to encode image", nil)
32
+ }
33
+ }
34
+ }
35
+
36
+ @objc func captureAndMaskWithOptions(_ options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
37
+ DispatchQueue.main.async {
38
+ guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else {
39
+ reject("NO_WINDOW", "Unable to get key window", nil)
40
+ return
41
+ }
42
+
43
+ UIGraphicsBeginImageContextWithOptions(window.bounds.size, false, UIScreen.main.scale)
44
+ window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
45
+ let screenshot = UIGraphicsGetImageFromCurrentImageContext()
46
+ UIGraphicsEndImageContext()
47
+
48
+ guard let image = screenshot else {
49
+ reject("CAPTURE_FAILED", "Failed to capture screen", nil)
50
+ return
51
+ }
52
+
53
+ // Apply masking with custom options
54
+ let maskedImage = self.applyMaskingWithOptions(to: image, in: window, options: options)
55
+
56
+ if let data = maskedImage.jpegData(compressionQuality: 0.5) {
57
+ let base64 = data.base64EncodedString()
58
+ resolve(base64)
59
+ } else {
60
+ reject("ENCODING_FAILED", "Failed to encode image", nil)
61
+ }
62
+ }
63
+ }
64
+
65
+ private func applyMasking(to image: UIImage, in window: UIWindow) -> UIImage {
66
+ return applyMaskingWithOptions(to: image, in: window, options: [:])
67
+ }
68
+
69
+ private func applyMaskingWithOptions(to image: UIImage, in window: UIWindow, options: NSDictionary) -> UIImage {
70
+ UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
71
+ guard let context = UIGraphicsGetCurrentContext() else { return image }
72
+
73
+ // Draw the original image
74
+ image.draw(in: CGRect(origin: .zero, size: image.size))
75
+
76
+ // Find and mask sensitive elements
77
+ let sensitiveElements = findSensitiveElements(in: window)
78
+
79
+ for element in sensitiveElements {
80
+ let frame = element.frame
81
+ let maskingType = getMaskingType(for: element)
82
+
83
+ switch maskingType {
84
+ case .blur:
85
+ applyBlurMask(in: context, frame: frame)
86
+ case .rectangle:
87
+ applyRectangleMask(in: context, frame: frame)
88
+ case .pixelate:
89
+ applyPixelateMask(in: context, frame: frame, image: image)
90
+ case .none:
91
+ break
92
+ }
93
+ }
94
+
95
+ let maskedImage = UIGraphicsGetImageFromCurrentImageContext() ?? image
96
+ UIGraphicsEndImageContext()
97
+
98
+ return maskedImage
99
+ }
100
+
101
+ private func findSensitiveElements(in view: UIView) -> [UIView] {
102
+ var sensitiveElements: [UIView] = []
103
+
104
+ func traverseView(_ view: UIView) {
105
+ // Check if this view should be masked
106
+ if shouldMaskView(view) {
107
+ sensitiveElements.append(view)
108
+ }
109
+
110
+ // Recursively check subviews
111
+ for subview in view.subviews {
112
+ traverseView(subview)
113
+ }
114
+ }
115
+
116
+ traverseView(view)
117
+ return sensitiveElements
118
+ }
119
+
120
+ private func shouldMaskView(_ view: UIView) -> Bool {
121
+ // Check for UITextField - mask all text fields when inputMasking is enabled
122
+ if view is UITextField {
123
+ return true
124
+ }
125
+
126
+ // Check for UITextView - mask all text views when inputMasking is enabled
127
+ if view is UITextView {
128
+ return true
129
+ }
130
+
131
+ return false
132
+ }
133
+
134
+ private func getMaskingType(for view: UIView) -> MaskingType {
135
+ // Default masking type for all text inputs
136
+ return .rectangle
137
+ }
138
+
139
+ private func applyBlurMask(in context: CGContext, frame: CGRect) {
140
+ // Create a blur effect
141
+ context.setFillColor(UIColor.black.withAlphaComponent(0.8).cgColor)
142
+ context.fill(frame)
143
+
144
+ // Add some noise to make it look blurred
145
+ context.setFillColor(UIColor.white.withAlphaComponent(0.3).cgColor)
146
+ for _ in 0..<20 {
147
+ let randomX = frame.origin.x + CGFloat.random(in: 0...frame.width)
148
+ let randomY = frame.origin.y + CGFloat.random(in: 0...frame.height)
149
+ let randomSize = CGFloat.random(in: 2...8)
150
+ context.fillEllipse(in: CGRect(x: randomX, y: randomY, width: randomSize, height: randomSize))
151
+ }
152
+ }
153
+
154
+ private func applyRectangleMask(in context: CGContext, frame: CGRect) {
155
+ // Simple rectangle fill
156
+ context.setFillColor(UIColor.gray.cgColor)
157
+ context.fill(frame)
158
+
159
+ // Add some text-like pattern
160
+ context.setFillColor(UIColor.darkGray.cgColor)
161
+ let lineHeight: CGFloat = 4
162
+ let spacing: CGFloat = 8
163
+
164
+ for i in stride(from: frame.origin.y + spacing, to: frame.origin.y + frame.height - spacing, by: lineHeight + spacing) {
165
+ let lineWidth = CGFloat.random(in: frame.width * 0.3...frame.width * 0.8)
166
+ let lineX = frame.origin.x + CGFloat.random(in: 0...(frame.width - lineWidth))
167
+ context.fill(CGRect(x: lineX, y: i, width: lineWidth, height: lineHeight))
168
+ }
169
+ }
170
+
171
+ private func applyPixelateMask(in context: CGContext, frame: CGRect, image: UIImage) {
172
+ // Create a pixelated effect
173
+ let pixelSize: CGFloat = 8
174
+ let pixelCountX = Int(frame.width / pixelSize)
175
+ let pixelCountY = Int(frame.height / pixelSize)
176
+
177
+ for x in 0..<pixelCountX {
178
+ for y in 0..<pixelCountY {
179
+ let pixelFrame = CGRect(
180
+ x: frame.origin.x + CGFloat(x) * pixelSize,
181
+ y: frame.origin.y + CGFloat(y) * pixelSize,
182
+ width: pixelSize,
183
+ height: pixelSize
184
+ )
185
+
186
+ // Use a random color for each pixel
187
+ let randomColor = UIColor(
188
+ red: CGFloat.random(in: 0...1),
189
+ green: CGFloat.random(in: 0...1),
190
+ blue: CGFloat.random(in: 0...1),
191
+ alpha: 1.0
192
+ )
193
+ context.setFillColor(randomColor.cgColor)
194
+ context.fill(pixelFrame)
195
+ }
196
+ }
197
+ }
198
+ }
199
+
200
+ private enum MaskingType {
201
+ case blur
202
+ case rectangle
203
+ case pixelate
204
+ case none
205
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplayer-app/session-recorder-react-native",
3
- "version": "0.0.1-beta.1",
3
+ "version": "0.0.1-beta.10",
4
4
  "description": "Multiplayer Fullstack Session Recorder for React Native",
5
5
  "author": {
6
6
  "name": "Multiplayer Software, Inc.",
@@ -21,10 +21,7 @@
21
21
  "import": "./dist/index.js",
22
22
  "require": "./dist/index.js"
23
23
  },
24
- "./expo": {
25
- "import": "./dist/expo.js",
26
- "require": "./dist/expo.js"
27
- }
24
+ "./app.plugin.js": "./app.plugin.js"
28
25
  },
29
26
  "engines": {
30
27
  "node": ">=18",
@@ -86,11 +83,29 @@
86
83
  },
87
84
  "peerDependencies": {
88
85
  "@opentelemetry/api": "^1.9.0",
89
- "expo": ">=49.0.0 <54.0.0",
90
86
  "react": ">=18.0.0 <20.0.0",
91
- "react-native": ">=0.72.0 <0.82.0"
87
+ "react-native": ">=0.72.0 <0.82.0",
88
+ "react-native-svg": "^15.13.0"
92
89
  },
93
90
  "optionalDependencies": {
94
91
  "expo-constants": "^15.0.0"
92
+ },
93
+ "react-native": {
94
+ "ios": {
95
+ "podspecPath": "./SessionRecorderNative.podspec"
96
+ },
97
+ "android": {
98
+ "sourceDir": "./android",
99
+ "packageImportPath": "import com.multiplayer.sessionrecorder.SessionRecorderPackage;"
100
+ }
101
+ },
102
+ "expo": {
103
+ "ios": {
104
+ "podspecPath": "./SessionRecorderNative.podspec"
105
+ },
106
+ "android": {
107
+ "sourceDir": "./android",
108
+ "packageImportPath": "import com.multiplayer.sessionrecorder.SessionRecorderPackage;"
109
+ }
95
110
  }
96
111
  }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@multiplayer-app/session-recorder-react-native-plugin",
3
+ "version": "0.0.1-beta.10",
4
+ "description": "Expo plugin for Session Recorder React Native",
5
+ "main": "src/index.js",
6
+ "keywords": [
7
+ "expo",
8
+ "plugin",
9
+ "session-recorder",
10
+ "react-native"
11
+ ],
12
+ "author": "Multiplayer Software, Inc.",
13
+ "license": "MIT",
14
+ "dependencies": {
15
+ "@expo/config-plugins": "^7.0.0"
16
+ },
17
+ "peerDependencies": {
18
+ "expo": ">=49.0.0"
19
+ }
20
+ }
@@ -0,0 +1,42 @@
1
+ const { withDangerousMod } = require('@expo/config-plugins')
2
+ const fs = require('fs')
3
+ const path = require('path')
4
+
5
+ const withSessionRecorder = (config) => {
6
+ return withDangerousMod(config, [
7
+ 'ios',
8
+ async (config) => {
9
+ const projectRoot = config.modRequest.projectRoot
10
+ const platformRoot = config.modRequest.platformProjectRoot
11
+
12
+ // Ensure the Podfile includes our native module
13
+ const podfilePath = path.join(platformRoot, 'Podfile')
14
+
15
+ if (fs.existsSync(podfilePath)) {
16
+ let podfileContent = fs.readFileSync(podfilePath, 'utf8')
17
+
18
+ // Check if our pod is already included
19
+ if (!podfileContent.includes('SessionRecorderNative')) {
20
+ // Add our pod to the Podfile
21
+ const podEntry = ` pod 'SessionRecorderNative', :path => '../node_modules/@multiplayer-app/session-recorder-react-native/ios'\n`
22
+
23
+ // Find the target section and add our pod
24
+ const targetRegex = /(target ['"][^'"]+['"] do)/
25
+ if (targetRegex.test(podfileContent)) {
26
+ podfileContent = podfileContent.replace(targetRegex, `$1\n${podEntry}`)
27
+ } else {
28
+ // If no target found, add at the end before the end statement
29
+ podfileContent = podfileContent.replace(/(end\s*$)/, `${podEntry}$1`)
30
+ }
31
+
32
+ fs.writeFileSync(podfilePath, podfileContent)
33
+ console.log('✅ Added SessionRecorderNative to Podfile')
34
+ }
35
+ }
36
+
37
+ return config
38
+ }
39
+ ])
40
+ }
41
+
42
+ module.exports = withSessionRecorder
@@ -0,0 +1,15 @@
1
+ module.exports = {
2
+ dependencies: {
3
+ '@multiplayer-app/session-recorder-react-native': {
4
+ platforms: {
5
+ android: {
6
+ sourceDir: './android',
7
+ packageImportPath: 'import com.multiplayer.sessionrecorder.SessionRecorderPackage;'
8
+ },
9
+ ios: {
10
+ podspecPath: './SessionRecorderNative.podspec'
11
+ }
12
+ }
13
+ }
14
+ }
15
+ }