@multiplayer-app/session-recorder-react-native 0.0.1-beta.7 → 0.0.1-beta.9
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/app.plugin.js +42 -0
- package/docs/NATIVE_MODULE_SETUP.md +177 -0
- package/ios/SessionRecorderNative.podspec +5 -0
- package/package.json +10 -1
- package/plugin/package.json +20 -0
- package/plugin/src/index.js +42 -0
- package/android/src/main/AndroidManifest.xml +0 -2
- package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingModule.kt +0 -202
- package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingPackage.kt +0 -16
- package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderModule.kt +0 -202
- package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderPackage.kt +0 -16
- package/babel.config.js +0 -13
- package/docs/AUTO_METADATA_DETECTION.md +0 -108
- package/docs/TROUBLESHOOTING.md +0 -168
- package/ios/ScreenMasking.m +0 -12
- package/ios/ScreenMasking.podspec +0 -21
- package/ios/ScreenMasking.swift +0 -205
- package/ios/SessionRecorder.podspec +0 -21
- package/scripts/generate-app-metadata.js +0 -173
- package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +0 -86
- package/src/components/GestureCaptureWrapper/index.ts +0 -1
- package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +0 -72
- package/src/components/ScreenRecorderView/index.ts +0 -1
- package/src/components/SessionRecorderWidget/FinalPopover.tsx +0 -62
- package/src/components/SessionRecorderWidget/FloatingButton.tsx +0 -136
- package/src/components/SessionRecorderWidget/InitialPopover.tsx +0 -89
- package/src/components/SessionRecorderWidget/ModalContainer.tsx +0 -128
- package/src/components/SessionRecorderWidget/ModalHeader.tsx +0 -24
- package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +0 -109
- package/src/components/SessionRecorderWidget/icons.tsx +0 -52
- package/src/components/SessionRecorderWidget/index.ts +0 -3
- package/src/components/SessionRecorderWidget/styles.ts +0 -150
- package/src/components/index.ts +0 -3
- package/src/config/constants.ts +0 -60
- package/src/config/defaults.ts +0 -83
- package/src/config/index.ts +0 -6
- package/src/config/masking.ts +0 -28
- package/src/config/session-recorder.ts +0 -55
- package/src/config/validators.ts +0 -31
- package/src/context/SessionRecorderContext.tsx +0 -53
- package/src/index.ts +0 -9
- package/src/native/ScreenMasking.ts +0 -34
- package/src/native/SessionRecorderNative.ts +0 -34
- package/src/otel/helpers.ts +0 -275
- package/src/otel/index.ts +0 -138
- package/src/otel/instrumentations/index.ts +0 -115
- package/src/patch/index.ts +0 -1
- package/src/patch/xhr.ts +0 -141
- package/src/recorder/eventExporter.ts +0 -141
- package/src/recorder/gestureRecorder.ts +0 -498
- package/src/recorder/index.ts +0 -179
- package/src/recorder/navigationTracker.ts +0 -449
- package/src/recorder/screenRecorder.ts +0 -527
- package/src/services/api.service.ts +0 -203
- package/src/services/screenMaskingService.ts +0 -118
- package/src/services/storage.service.ts +0 -199
- package/src/session-recorder.ts +0 -606
- package/src/types/expo.d.ts +0 -23
- package/src/types/index.ts +0 -28
- package/src/types/session-recorder.ts +0 -429
- package/src/types/session.ts +0 -65
- package/src/utils/app-metadata.ts +0 -31
- package/src/utils/index.ts +0 -8
- package/src/utils/logger.ts +0 -225
- package/src/utils/nativeModuleTest.ts +0 -60
- package/src/utils/platform.ts +0 -384
- package/src/utils/request-utils.ts +0 -61
- package/src/utils/rrweb-events.ts +0 -309
- package/src/utils/session.ts +0 -18
- package/src/utils/time.ts +0 -17
- package/src/utils/type-utils.ts +0 -75
- package/src/version.ts +0 -1
- package/tsconfig.json +0 -24
- /package/ios/{SessionRecorder.m → SessionRecorderNative.m} +0 -0
- /package/ios/{SessionRecorder.swift → SessionRecorderNative.swift} +0 -0
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
package com.multiplayer.sessionrecorder
|
|
2
|
-
|
|
3
|
-
import android.app.Activity
|
|
4
|
-
import android.graphics.*
|
|
5
|
-
import android.util.Base64
|
|
6
|
-
import android.view.View
|
|
7
|
-
import android.widget.EditText
|
|
8
|
-
import android.widget.TextView
|
|
9
|
-
import com.facebook.react.bridge.*
|
|
10
|
-
import java.io.ByteArrayOutputStream
|
|
11
|
-
|
|
12
|
-
class SessionRecorderModule(reactContext: ReactApplicationContext) :
|
|
13
|
-
ReactContextBaseJavaModule(reactContext) {
|
|
14
|
-
|
|
15
|
-
override fun getName() = "SessionRecorderNative"
|
|
16
|
-
|
|
17
|
-
@ReactMethod
|
|
18
|
-
fun captureAndMask(promise: Promise) {
|
|
19
|
-
val activity = currentActivity ?: return promise.reject("NO_ACTIVITY", "No activity found")
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
val maskedImage = captureAndMaskScreen(activity, null)
|
|
23
|
-
promise.resolve(maskedImage)
|
|
24
|
-
} catch (e: Exception) {
|
|
25
|
-
promise.reject("CAPTURE_FAILED", "Failed to capture and mask screen", e)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@ReactMethod
|
|
30
|
-
fun captureAndMaskWithOptions(options: ReadableMap, promise: Promise) {
|
|
31
|
-
val activity = currentActivity ?: return promise.reject("NO_ACTIVITY", "No activity found")
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
val maskedImage = captureAndMaskScreen(activity, options)
|
|
35
|
-
promise.resolve(maskedImage)
|
|
36
|
-
} catch (e: Exception) {
|
|
37
|
-
promise.reject("CAPTURE_FAILED", "Failed to capture and mask screen", e)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private fun captureAndMaskScreen(activity: Activity, options: ReadableMap?): String {
|
|
42
|
-
val rootView = activity.window.decorView.rootView
|
|
43
|
-
|
|
44
|
-
// Enable drawing cache
|
|
45
|
-
rootView.isDrawingCacheEnabled = true
|
|
46
|
-
rootView.buildDrawingCache()
|
|
47
|
-
|
|
48
|
-
val bitmap = Bitmap.createBitmap(rootView.drawingCache)
|
|
49
|
-
rootView.isDrawingCacheEnabled = false
|
|
50
|
-
|
|
51
|
-
// Apply masking
|
|
52
|
-
val maskedBitmap = applyMasking(bitmap, rootView, options)
|
|
53
|
-
|
|
54
|
-
// Convert to base64
|
|
55
|
-
val output = ByteArrayOutputStream()
|
|
56
|
-
maskedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, output)
|
|
57
|
-
val base64 = Base64.encodeToString(output.toByteArray(), Base64.DEFAULT)
|
|
58
|
-
|
|
59
|
-
return base64
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private fun applyMasking(bitmap: Bitmap, rootView: View, options: ReadableMap?): Bitmap {
|
|
63
|
-
val maskedBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
|
|
64
|
-
val canvas = Canvas(maskedBitmap)
|
|
65
|
-
|
|
66
|
-
// Find sensitive elements
|
|
67
|
-
val sensitiveElements = findSensitiveElements(rootView)
|
|
68
|
-
|
|
69
|
-
for (element in sensitiveElements) {
|
|
70
|
-
val location = IntArray(2)
|
|
71
|
-
element.getLocationOnScreen(location)
|
|
72
|
-
|
|
73
|
-
val frame = Rect(
|
|
74
|
-
location[0],
|
|
75
|
-
location[1],
|
|
76
|
-
location[0] + element.width,
|
|
77
|
-
location[1] + element.height
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
val maskingType = getMaskingType(element)
|
|
81
|
-
|
|
82
|
-
when (maskingType) {
|
|
83
|
-
MaskingType.BLUR -> applyBlurMask(canvas, frame)
|
|
84
|
-
MaskingType.RECTANGLE -> applyRectangleMask(canvas, frame)
|
|
85
|
-
MaskingType.PIXELATE -> applyPixelateMask(canvas, frame)
|
|
86
|
-
MaskingType.NONE -> { /* No masking */ }
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return maskedBitmap
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private fun findSensitiveElements(view: View): List<View> {
|
|
94
|
-
val sensitiveElements = mutableListOf<View>()
|
|
95
|
-
|
|
96
|
-
fun traverseView(currentView: View) {
|
|
97
|
-
if (shouldMaskView(currentView)) {
|
|
98
|
-
sensitiveElements.add(currentView)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (currentView is ViewGroup) {
|
|
102
|
-
for (i in 0 until currentView.childCount) {
|
|
103
|
-
traverseView(currentView.getChildAt(i))
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
traverseView(view)
|
|
109
|
-
return sensitiveElements
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private fun shouldMaskView(view: View): Boolean {
|
|
113
|
-
// Check for EditText - mask all text fields when inputMasking is enabled
|
|
114
|
-
if (view is EditText) {
|
|
115
|
-
return true
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Check for TextView - mask all text views when inputMasking is enabled
|
|
119
|
-
if (view is TextView) {
|
|
120
|
-
return true
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return false
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private fun getMaskingType(view: View): MaskingType {
|
|
127
|
-
// Default masking type for all text inputs
|
|
128
|
-
return MaskingType.RECTANGLE
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private fun applyBlurMask(canvas: Canvas, frame: Rect) {
|
|
132
|
-
val paint = Paint().apply {
|
|
133
|
-
color = Color.BLACK
|
|
134
|
-
alpha = 200 // Semi-transparent
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
canvas.drawRect(frame, paint)
|
|
138
|
-
|
|
139
|
-
// Add some noise to make it look blurred
|
|
140
|
-
paint.color = Color.WHITE
|
|
141
|
-
paint.alpha = 80
|
|
142
|
-
|
|
143
|
-
for (i in 0..20) {
|
|
144
|
-
val randomX = frame.left + (Math.random() * frame.width()).toFloat()
|
|
145
|
-
val randomY = frame.top + (Math.random() * frame.height()).toFloat()
|
|
146
|
-
val randomSize = (Math.random() * 6 + 2).toFloat()
|
|
147
|
-
canvas.drawCircle(randomX, randomY, randomSize, paint)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private fun applyRectangleMask(canvas: Canvas, frame: Rect) {
|
|
152
|
-
val paint = Paint().apply {
|
|
153
|
-
color = Color.GRAY
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
canvas.drawRect(frame, paint)
|
|
157
|
-
|
|
158
|
-
// Add some text-like pattern
|
|
159
|
-
paint.color = Color.DKGRAY
|
|
160
|
-
val lineHeight = 4f
|
|
161
|
-
val spacing = 8f
|
|
162
|
-
|
|
163
|
-
var y = frame.top + spacing
|
|
164
|
-
while (y < frame.bottom - spacing) {
|
|
165
|
-
val lineWidth = (Math.random() * frame.width() * 0.5 + frame.width() * 0.3).toFloat()
|
|
166
|
-
val x = frame.left + (Math.random() * (frame.width() - lineWidth)).toFloat()
|
|
167
|
-
canvas.drawRect(x, y, x + lineWidth, y + lineHeight, paint)
|
|
168
|
-
y += lineHeight + spacing
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
private fun applyPixelateMask(canvas: Canvas, frame: Rect) {
|
|
173
|
-
val pixelSize = 8f
|
|
174
|
-
val pixelCountX = (frame.width() / pixelSize).toInt()
|
|
175
|
-
val pixelCountY = (frame.height() / pixelSize).toInt()
|
|
176
|
-
|
|
177
|
-
val paint = Paint()
|
|
178
|
-
|
|
179
|
-
for (x in 0 until pixelCountX) {
|
|
180
|
-
for (y in 0 until pixelCountY) {
|
|
181
|
-
val pixelFrame = RectF(
|
|
182
|
-
frame.left + x * pixelSize,
|
|
183
|
-
frame.top + y * pixelSize,
|
|
184
|
-
frame.left + (x + 1) * pixelSize,
|
|
185
|
-
frame.top + (y + 1) * pixelSize
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
// Use a random color for each pixel
|
|
189
|
-
paint.color = Color.rgb(
|
|
190
|
-
(Math.random() * 255).toInt(),
|
|
191
|
-
(Math.random() * 255).toInt(),
|
|
192
|
-
(Math.random() * 255).toInt()
|
|
193
|
-
)
|
|
194
|
-
canvas.drawRect(pixelFrame, paint)
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
private enum class MaskingType {
|
|
200
|
-
BLUR, RECTANGLE, PIXELATE, NONE
|
|
201
|
-
}
|
|
202
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
package com.multiplayer.sessionrecorder
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.ReactPackage
|
|
4
|
-
import com.facebook.react.bridge.NativeModule
|
|
5
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
-
import com.facebook.react.uimanager.ViewManager
|
|
7
|
-
|
|
8
|
-
class SessionRecorderPackage : ReactPackage {
|
|
9
|
-
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
|
-
return listOf(SessionRecorderModule(reactContext))
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
14
|
-
return emptyList()
|
|
15
|
-
}
|
|
16
|
-
}
|
package/babel.config.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
presets: [
|
|
3
|
-
'@react-native/babel-preset',
|
|
4
|
-
'@babel/preset-env',
|
|
5
|
-
'@babel/preset-typescript',
|
|
6
|
-
['@babel/preset-react', { runtime: 'automatic' }],
|
|
7
|
-
],
|
|
8
|
-
plugins: [
|
|
9
|
-
['@babel/plugin-transform-private-property-in-object', { loose: true }],
|
|
10
|
-
['@babel/plugin-transform-private-methods', { loose: true }],
|
|
11
|
-
['@babel/plugin-transform-class-properties', { loose: true }],
|
|
12
|
-
],
|
|
13
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# Automatic App Metadata Detection
|
|
2
|
-
|
|
3
|
-
The session recorder automatically detects app metadata from your project configuration files **without requiring any developer intervention**.
|
|
4
|
-
|
|
5
|
-
## How It Works
|
|
6
|
-
|
|
7
|
-
The library automatically extracts app information from common configuration files in this priority order:
|
|
8
|
-
|
|
9
|
-
1. **`app.json`** - Expo/React Native app configuration
|
|
10
|
-
2. **`app.config.js`** - Dynamic Expo configuration
|
|
11
|
-
3. **`package.json`** - Node.js package configuration (fallback)
|
|
12
|
-
|
|
13
|
-
## Supported Configuration Files
|
|
14
|
-
|
|
15
|
-
### app.json
|
|
16
|
-
|
|
17
|
-
```json
|
|
18
|
-
{
|
|
19
|
-
"name": "My Awesome App",
|
|
20
|
-
"version": "1.2.3",
|
|
21
|
-
"displayName": "My App",
|
|
22
|
-
"ios": {
|
|
23
|
-
"bundleIdentifier": "com.mycompany.myapp",
|
|
24
|
-
"buildNumber": "123"
|
|
25
|
-
},
|
|
26
|
-
"android": {
|
|
27
|
-
"package": "com.mycompany.myapp",
|
|
28
|
-
"versionCode": 123
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### app.config.js
|
|
34
|
-
|
|
35
|
-
```javascript
|
|
36
|
-
export default {
|
|
37
|
-
name: 'My Awesome App',
|
|
38
|
-
version: '1.2.3',
|
|
39
|
-
displayName: 'My App',
|
|
40
|
-
ios: {
|
|
41
|
-
bundleIdentifier: 'com.mycompany.myapp',
|
|
42
|
-
buildNumber: '123'
|
|
43
|
-
},
|
|
44
|
-
android: {
|
|
45
|
-
package: 'com.mycompany.myapp',
|
|
46
|
-
versionCode: 123
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### package.json (fallback)
|
|
52
|
-
|
|
53
|
-
```json
|
|
54
|
-
{
|
|
55
|
-
"name": "my-awesome-app",
|
|
56
|
-
"version": "1.2.3"
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Detected Metadata
|
|
61
|
-
|
|
62
|
-
The following information is automatically extracted:
|
|
63
|
-
|
|
64
|
-
- **App Name** - From `name` or `displayName`
|
|
65
|
-
- **App Version** - From `version`
|
|
66
|
-
- **Bundle ID** - From `ios.bundleIdentifier` or `android.package`
|
|
67
|
-
- **Build Number** - From `ios.buildNumber` or `android.versionCode`
|
|
68
|
-
|
|
69
|
-
## Build Process
|
|
70
|
-
|
|
71
|
-
The metadata detection happens automatically during the build process:
|
|
72
|
-
|
|
73
|
-
1. The build script scans your project root for configuration files
|
|
74
|
-
2. Extracts relevant metadata from the first found configuration
|
|
75
|
-
3. Generates a TypeScript file with the detected metadata
|
|
76
|
-
4. The session recorder uses this metadata in all recordings
|
|
77
|
-
|
|
78
|
-
## No Developer Action Required
|
|
79
|
-
|
|
80
|
-
✅ **Zero configuration** - Works out of the box
|
|
81
|
-
✅ **Automatic detection** - Scans common config files
|
|
82
|
-
✅ **Build-time generation** - No runtime file reading
|
|
83
|
-
✅ **Fallback support** - Graceful degradation
|
|
84
|
-
|
|
85
|
-
## Manual Override (Optional)
|
|
86
|
-
|
|
87
|
-
If you need to override the auto-detected metadata, you can still use the manual configuration:
|
|
88
|
-
|
|
89
|
-
```typescript
|
|
90
|
-
import { configureAppMetadata } from '@multiplayer-app/session-recorder-react-native'
|
|
91
|
-
|
|
92
|
-
configureAppMetadata({
|
|
93
|
-
name: 'Custom App Name',
|
|
94
|
-
version: '2.0.0',
|
|
95
|
-
bundleId: 'com.custom.app'
|
|
96
|
-
})
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## Priority Order
|
|
100
|
-
|
|
101
|
-
The metadata detection follows this priority:
|
|
102
|
-
|
|
103
|
-
1. **Expo Config** (if using Expo)
|
|
104
|
-
2. **Manual Configuration** (if `configureAppMetadata` was called)
|
|
105
|
-
3. **Auto-detected Metadata** (from config files)
|
|
106
|
-
4. **Fallback Values** (defaults)
|
|
107
|
-
|
|
108
|
-
This ensures maximum compatibility across different React Native setups while providing rich metadata for debugging sessions.
|
package/docs/TROUBLESHOOTING.md
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
# Troubleshooting Native Module Issues
|
|
2
|
-
|
|
3
|
-
If you're seeing the warning "Screen masking native module is not available - auto-linking may still be in progress", follow these steps:
|
|
4
|
-
|
|
5
|
-
## Quick Fix
|
|
6
|
-
|
|
7
|
-
1. **Clean and Rebuild**:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# Clean React Native cache
|
|
11
|
-
npx react-native start --reset-cache
|
|
12
|
-
|
|
13
|
-
# For iOS
|
|
14
|
-
cd ios && pod install && cd ..
|
|
15
|
-
npx react-native run-ios
|
|
16
|
-
|
|
17
|
-
# For Android
|
|
18
|
-
npx react-native run-android
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
2. **Check Auto-Linking**:
|
|
22
|
-
```bash
|
|
23
|
-
# Check if auto-linking detected the module
|
|
24
|
-
npx react-native config
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Detailed Troubleshooting
|
|
28
|
-
|
|
29
|
-
### 1. Verify Package Installation
|
|
30
|
-
|
|
31
|
-
Make sure the package is properly installed:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npm list @multiplayer-app/session-recorder-react-native
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### 2. Check Native Module Registration
|
|
38
|
-
|
|
39
|
-
The native module should be named `SessionRecorderNative`. You can verify this by checking the logs for:
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
Available native modules: [..., SessionRecorderNative, ...]
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 3. iOS Specific Issues
|
|
46
|
-
|
|
47
|
-
**Check Podfile**:
|
|
48
|
-
|
|
49
|
-
```ruby
|
|
50
|
-
# In your ios/Podfile, you should see:
|
|
51
|
-
pod 'SessionRecorderNative', :path => '../node_modules/@multiplayer-app/session-recorder-react-native/ios'
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
**Clean iOS Build**:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
cd ios
|
|
58
|
-
rm -rf build
|
|
59
|
-
rm -rf Pods
|
|
60
|
-
rm Podfile.lock
|
|
61
|
-
pod install
|
|
62
|
-
cd ..
|
|
63
|
-
npx react-native run-ios
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### 4. Android Specific Issues
|
|
67
|
-
|
|
68
|
-
**Check MainApplication.java**:
|
|
69
|
-
|
|
70
|
-
```java
|
|
71
|
-
// Should be automatically added by auto-linking:
|
|
72
|
-
import com.multiplayer.sessionrecorder.SessionRecorderPackage;
|
|
73
|
-
|
|
74
|
-
// In getPackages():
|
|
75
|
-
new SessionRecorderPackage()
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
**Clean Android Build**:
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
cd android
|
|
82
|
-
./gradlew clean
|
|
83
|
-
cd ..
|
|
84
|
-
npx react-native run-android
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### 5. Manual Linking (If Auto-Linking Fails)
|
|
88
|
-
|
|
89
|
-
If auto-linking doesn't work, you can manually link:
|
|
90
|
-
|
|
91
|
-
**iOS**:
|
|
92
|
-
|
|
93
|
-
1. Add to `ios/Podfile`:
|
|
94
|
-
```ruby
|
|
95
|
-
pod 'SessionRecorderNative', :path => '../node_modules/@multiplayer-app/session-recorder-react-native/ios'
|
|
96
|
-
```
|
|
97
|
-
2. Run `pod install`
|
|
98
|
-
|
|
99
|
-
**Android**:
|
|
100
|
-
|
|
101
|
-
1. Add to `android/settings.gradle`:
|
|
102
|
-
```gradle
|
|
103
|
-
include ':react-native-session-recorder'
|
|
104
|
-
project(':react-native-session-recorder').projectDir = new File(rootProject.projectDir, '../node_modules/@multiplayer-app/session-recorder-react-native/android')
|
|
105
|
-
```
|
|
106
|
-
2. Add to `android/app/build.gradle`:
|
|
107
|
-
```gradle
|
|
108
|
-
dependencies {
|
|
109
|
-
implementation project(':react-native-session-recorder')
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
3. Add to `MainApplication.java`:
|
|
113
|
-
|
|
114
|
-
```java
|
|
115
|
-
import com.multiplayer.sessionrecorder.SessionRecorderPackage;
|
|
116
|
-
|
|
117
|
-
@Override
|
|
118
|
-
protected List<ReactPackage> getPackages() {
|
|
119
|
-
return Arrays.<ReactPackage>asList(
|
|
120
|
-
new MainReactPackage(),
|
|
121
|
-
new SessionRecorderPackage()
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### 6. Debug Native Module Availability
|
|
127
|
-
|
|
128
|
-
Add this to your app to debug:
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
import { testNativeModuleAvailability } from '@multiplayer-app/session-recorder-react-native/utils'
|
|
132
|
-
|
|
133
|
-
// Call this in your app
|
|
134
|
-
testNativeModuleAvailability()
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
This will log detailed information about native module availability.
|
|
138
|
-
|
|
139
|
-
### 7. Common Issues
|
|
140
|
-
|
|
141
|
-
**Issue**: "Module not found" error
|
|
142
|
-
**Solution**: Ensure the package is installed and auto-linking is working
|
|
143
|
-
|
|
144
|
-
**Issue**: "Method not available" error
|
|
145
|
-
**Solution**: The native module is linked but methods aren't exposed. Check the native code.
|
|
146
|
-
|
|
147
|
-
**Issue**: Auto-linking not working
|
|
148
|
-
**Solution**: Check `react-native.config.js` and ensure React Native version is 0.60+
|
|
149
|
-
|
|
150
|
-
**Issue**: Pod install fails
|
|
151
|
-
**Solution**: Update CocoaPods: `sudo gem install cocoapods`
|
|
152
|
-
|
|
153
|
-
### 8. Verification
|
|
154
|
-
|
|
155
|
-
Once fixed, you should see:
|
|
156
|
-
|
|
157
|
-
```
|
|
158
|
-
✅ SessionRecorderNative module is available!
|
|
159
|
-
✅ captureAndMask method is available!
|
|
160
|
-
✅ captureAndMaskWithOptions method is available!
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## Still Having Issues?
|
|
164
|
-
|
|
165
|
-
1. Check the [React Native Auto-Linking documentation](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md)
|
|
166
|
-
2. Verify your React Native version (0.60+ required)
|
|
167
|
-
3. Check for conflicting native modules
|
|
168
|
-
4. Try creating a fresh React Native project and testing the module there
|
package/ios/ScreenMasking.m
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#import <React/RCTBridgeModule.h>
|
|
2
|
-
|
|
3
|
-
@interface RCT_EXTERN_MODULE(SessionRecorder, 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
|
-
@end
|
|
@@ -1,21 +0,0 @@
|
|
|
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 = "SessionRecorder"
|
|
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
|
-
end
|