@multiplayer-app/session-recorder-react-native 1.0.1-beta.2 → 1.0.1-beta.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/REACT_NATIVE_SETUP.md +91 -0
- package/android/build.gradle +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/ios/GestureTargetFinder.swift +50 -0
- package/ios/SessionRecorderNative.swift +10 -1
- package/ios/SessionRecorderNativeSpec.swift +55 -0
- package/package.json +14 -2
- package/react-native.config.js +13 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# React Native Setup Guide
|
|
2
|
+
|
|
3
|
+
This package provides native session recording capabilities for React Native applications with automatic masking of sensitive UI elements.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### For React Native CLI projects:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @multiplayer-app/session-recorder-react-native
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### For Expo projects:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx expo install @multiplayer-app/session-recorder-react-native
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Platform-specific Setup
|
|
20
|
+
|
|
21
|
+
### Android
|
|
22
|
+
|
|
23
|
+
The Android native module is automatically linked. No additional setup required.
|
|
24
|
+
|
|
25
|
+
### iOS
|
|
26
|
+
|
|
27
|
+
The iOS native module uses CocoaPods for dependency management. Run:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd ios && pod install
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { SessionRecorder } from '@multiplayer-app/session-recorder-react-native'
|
|
37
|
+
|
|
38
|
+
// Initialize the session recorder
|
|
39
|
+
const sessionRecorder = new SessionRecorder({
|
|
40
|
+
// Configuration options
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Start recording
|
|
44
|
+
await sessionRecorder.start()
|
|
45
|
+
|
|
46
|
+
// Stop recording
|
|
47
|
+
await sessionRecorder.stop()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Native Module Structure
|
|
51
|
+
|
|
52
|
+
### Android
|
|
53
|
+
|
|
54
|
+
- `SessionRecorderNativeModule.kt` - Main native module implementation
|
|
55
|
+
- `SessionRecorderNativePackage.kt` - React Native package registration
|
|
56
|
+
- `SessionRecorderNativeConfig.kt` - Configuration class
|
|
57
|
+
- `ViewUtils.kt` - View utility functions
|
|
58
|
+
- `TargetInfo.kt` - Data model for target information
|
|
59
|
+
|
|
60
|
+
### iOS
|
|
61
|
+
|
|
62
|
+
- `SessionRecorderNative.swift` - Main native module implementation
|
|
63
|
+
- `GestureTargetFinder.swift` - Gesture target detection utilities
|
|
64
|
+
- `SessionRecorderNative.m` - Objective-C bridge
|
|
65
|
+
- `SessionRecorderNative.podspec` - CocoaPods specification
|
|
66
|
+
|
|
67
|
+
## Features
|
|
68
|
+
|
|
69
|
+
- **Screen Capture**: Capture screenshots with automatic masking
|
|
70
|
+
- **Gesture Recording**: Record user interactions (tap, pan, long press, pinch)
|
|
71
|
+
- **Sensitive Content Masking**: Automatically mask text inputs, images, and other sensitive elements
|
|
72
|
+
- **Cross-platform**: Works on both Android and iOS
|
|
73
|
+
- **Expo Compatible**: Supports Expo managed workflow
|
|
74
|
+
|
|
75
|
+
## Troubleshooting
|
|
76
|
+
|
|
77
|
+
### Android Build Issues
|
|
78
|
+
|
|
79
|
+
- Ensure Android SDK is properly configured
|
|
80
|
+
- Check that the package is properly linked in `MainApplication.java`
|
|
81
|
+
|
|
82
|
+
### iOS Build Issues
|
|
83
|
+
|
|
84
|
+
- Run `pod install` in the iOS directory
|
|
85
|
+
- Ensure Xcode is properly configured
|
|
86
|
+
- Check that the podspec is correctly configured
|
|
87
|
+
|
|
88
|
+
### Expo Issues
|
|
89
|
+
|
|
90
|
+
- Use `npx expo install` instead of `npm install`
|
|
91
|
+
- Ensure you're using a compatible Expo SDK version
|
package/android/build.gradle
CHANGED
|
@@ -94,4 +94,7 @@ dependencies {
|
|
|
94
94
|
//noinspection GradleDynamicVersion
|
|
95
95
|
implementation "com.facebook.react:react-native:+"
|
|
96
96
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
97
|
+
|
|
98
|
+
// TurboModule support
|
|
99
|
+
implementation "com.facebook.react:react-native-codegen:+"
|
|
97
100
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.0.1-beta.
|
|
1
|
+
export declare const version = "1.0.1-beta.3";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.version=void 0;var version=exports.version="1.0.1-beta.
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.version=void 0;var version=exports.version="1.0.1-beta.3";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
struct GestureTargetInfo {
|
|
4
|
+
let identifier: String
|
|
5
|
+
let label: String?
|
|
6
|
+
let role: String?
|
|
7
|
+
let testId: String?
|
|
8
|
+
let text: String?
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
enum GestureTargetFinder {
|
|
12
|
+
static func findTargetView(at point: CGPoint, in view: UIView) -> GestureTargetInfo {
|
|
13
|
+
let targetView = view.hitTest(point, with: nil)
|
|
14
|
+
|
|
15
|
+
guard let target = targetView else {
|
|
16
|
+
return GestureTargetInfo(identifier: "unknown", label: nil, role: nil, testId: nil, text: nil)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let identifier = target.accessibilityIdentifier ?? target.accessibilityLabel ?? "view-\(target.hash)"
|
|
20
|
+
let label = target.accessibilityLabel
|
|
21
|
+
let role = roleFromAccessibilityTraits(target.accessibilityTraits)
|
|
22
|
+
let testId = target.accessibilityIdentifier
|
|
23
|
+
|
|
24
|
+
var text: String?
|
|
25
|
+
if let labelView = target as? UILabel {
|
|
26
|
+
text = labelView.text
|
|
27
|
+
} else if let button = target as? UIButton {
|
|
28
|
+
text = button.titleLabel?.text
|
|
29
|
+
} else if let textField = target as? UITextField {
|
|
30
|
+
text = textField.text ?? textField.placeholder
|
|
31
|
+
} else if let textView = target as? UITextView {
|
|
32
|
+
text = textView.text
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return GestureTargetInfo(identifier: identifier, label: label, role: role, testId: testId, text: text)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static func roleFromAccessibilityTraits(_ traits: UIAccessibilityTraits) -> String? {
|
|
39
|
+
if traits.contains(.button) { return "button" }
|
|
40
|
+
if traits.contains(.link) { return "link" }
|
|
41
|
+
if traits.contains(.image) { return "image" }
|
|
42
|
+
if traits.contains(.staticText) { return "text" }
|
|
43
|
+
if traits.contains(.header) { return "header" }
|
|
44
|
+
if traits.contains(.searchField) { return "search" }
|
|
45
|
+
if traits.contains(.keyboardKey) { return "key" }
|
|
46
|
+
if traits.contains(.adjustable) { return "adjustable" }
|
|
47
|
+
if traits.contains(.tabBar) { return "tabbar" }
|
|
48
|
+
return nil
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -3,7 +3,7 @@ import React
|
|
|
3
3
|
import WebKit
|
|
4
4
|
|
|
5
5
|
@objc(SessionRecorderNative)
|
|
6
|
-
class SessionRecorderNative: RCTEventEmitter {
|
|
6
|
+
class SessionRecorderNative: RCTEventEmitter, UIGestureRecognizerDelegate {
|
|
7
7
|
|
|
8
8
|
// Configuration options
|
|
9
9
|
private var maskTextInputs: Bool = true
|
|
@@ -755,6 +755,15 @@ class SessionRecorderNative: RCTEventEmitter {
|
|
|
755
755
|
self.sendEvent(withName: "onGestureDetected", body: gestureEvent)
|
|
756
756
|
if let cb = gestureCallback { cb([gestureEvent]) }
|
|
757
757
|
}
|
|
758
|
+
|
|
759
|
+
// MARK: - UIGestureRecognizerDelegate
|
|
760
|
+
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
761
|
+
return true
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
|
|
765
|
+
return true
|
|
766
|
+
}
|
|
758
767
|
}
|
|
759
768
|
|
|
760
769
|
private enum MaskingType {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import React
|
|
3
|
+
|
|
4
|
+
@objc(SessionRecorderNativeSpec)
|
|
5
|
+
class SessionRecorderNativeSpec: NSObject, RCTBridgeModule {
|
|
6
|
+
|
|
7
|
+
static func moduleName() -> String! {
|
|
8
|
+
return "SessionRecorderNative"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static func requiresMainQueueSetup() -> Bool {
|
|
12
|
+
return true
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@objc func captureAndMask(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
16
|
+
// Implementation will be provided by the actual module
|
|
17
|
+
reject("NOT_IMPLEMENTED", "Method not implemented", nil)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@objc func captureAndMaskWithOptions(_ options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
21
|
+
// Implementation will be provided by the actual module
|
|
22
|
+
reject("NOT_IMPLEMENTED", "Method not implemented", nil)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@objc func startGestureRecording(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
26
|
+
// Implementation will be provided by the actual module
|
|
27
|
+
reject("NOT_IMPLEMENTED", "Method not implemented", nil)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@objc func stopGestureRecording(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
31
|
+
// Implementation will be provided by the actual module
|
|
32
|
+
reject("NOT_IMPLEMENTED", "Method not implemented", nil)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@objc func isGestureRecordingActive(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
36
|
+
// Implementation will be provided by the actual module
|
|
37
|
+
reject("NOT_IMPLEMENTED", "Method not implemented", nil)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@objc func setGestureCallback(_ callback: @escaping RCTResponseSenderBlock) {
|
|
41
|
+
// Implementation will be provided by the actual module
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@objc func recordGesture(_ gestureType: String, x: NSNumber, y: NSNumber, target: String?, metadata: NSDictionary?) {
|
|
45
|
+
// Implementation will be provided by the actual module
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@objc func addListener(_ eventName: String) {
|
|
49
|
+
// Required for RN event emitter contracts
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@objc func removeListeners(_ count: Int) {
|
|
53
|
+
// Required for RN event emitter contracts
|
|
54
|
+
}
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplayer-app/session-recorder-react-native",
|
|
3
|
-
"version": "1.0.1-beta.
|
|
3
|
+
"version": "1.0.1-beta.3",
|
|
4
4
|
"description": "Multiplayer Fullstack Session Recorder for React Native",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Multiplayer Software, Inc.",
|
|
@@ -35,7 +35,10 @@
|
|
|
35
35
|
"otlp",
|
|
36
36
|
"fullstack session recorder",
|
|
37
37
|
"react-native",
|
|
38
|
-
"expo"
|
|
38
|
+
"expo",
|
|
39
|
+
"native-module",
|
|
40
|
+
"screen-capture",
|
|
41
|
+
"gesture-recording"
|
|
39
42
|
],
|
|
40
43
|
"scripts": {
|
|
41
44
|
"clean": "rimraf dist",
|
|
@@ -93,5 +96,14 @@
|
|
|
93
96
|
"expo-constants": {
|
|
94
97
|
"optional": true
|
|
95
98
|
}
|
|
99
|
+
},
|
|
100
|
+
"react-native": {
|
|
101
|
+
"android": {
|
|
102
|
+
"sourceDir": "./android",
|
|
103
|
+
"packageImportPath": "import com.multiplayer.sessionrecordernative.SessionRecorderNativePackage;"
|
|
104
|
+
},
|
|
105
|
+
"ios": {
|
|
106
|
+
"project": "./ios/SessionRecorderNative.xcodeproj"
|
|
107
|
+
}
|
|
96
108
|
}
|
|
97
109
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
dependency: {
|
|
3
|
+
platforms: {
|
|
4
|
+
android: {
|
|
5
|
+
sourceDir: '../android',
|
|
6
|
+
packageImportPath: 'import com.multiplayer.sessionrecordernative.SessionRecorderNativePackage;'
|
|
7
|
+
},
|
|
8
|
+
ios: {
|
|
9
|
+
project: './ios/SessionRecorderNative.xcodeproj'
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|