@multiplayer-app/session-recorder-react-native 0.0.1 → 1.0.0-beta.1
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 +708 -83
- package/SessionRecorderNative.podspec +26 -0
- package/android/build.gradle +34 -0
- package/copy-react-native-dist.sh +34 -16
- package/dist/components/ScreenRecorderView/ScreenRecorderView.js +1 -1
- package/dist/components/ScreenRecorderView/ScreenRecorderView.js.map +1 -1
- package/dist/components/SessionRecorderWidget/ErrorBanner.d.ts +7 -0
- package/dist/components/SessionRecorderWidget/ErrorBanner.js +1 -0
- package/dist/components/SessionRecorderWidget/ErrorBanner.js.map +1 -0
- package/dist/components/SessionRecorderWidget/FinalPopover.d.ts +12 -0
- package/dist/components/SessionRecorderWidget/FinalPopover.js +1 -0
- package/dist/components/SessionRecorderWidget/FinalPopover.js.map +1 -0
- package/dist/components/SessionRecorderWidget/FloatingButton.d.ts +8 -0
- package/dist/components/SessionRecorderWidget/FloatingButton.js +1 -0
- package/dist/components/SessionRecorderWidget/FloatingButton.js.map +1 -0
- package/dist/components/SessionRecorderWidget/InitialPopover.d.ts +16 -0
- package/dist/components/SessionRecorderWidget/InitialPopover.js +1 -0
- package/dist/components/SessionRecorderWidget/InitialPopover.js.map +1 -0
- package/dist/components/SessionRecorderWidget/ModalContainer.d.ts +8 -0
- package/dist/components/SessionRecorderWidget/ModalContainer.js +1 -0
- package/dist/components/SessionRecorderWidget/ModalContainer.js.map +1 -0
- package/dist/components/SessionRecorderWidget/ModalHeader.d.ts +6 -0
- package/dist/components/SessionRecorderWidget/ModalHeader.js +1 -0
- package/dist/components/SessionRecorderWidget/ModalHeader.js.map +1 -0
- package/dist/components/SessionRecorderWidget/SessionRecorderWidget.d.ts +5 -0
- package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js +1 -0
- package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -0
- package/dist/components/SessionRecorderWidget/icons.d.ts +11 -0
- package/dist/components/SessionRecorderWidget/icons.js +1 -0
- package/dist/components/SessionRecorderWidget/icons.js.map +1 -0
- package/dist/components/SessionRecorderWidget/index.d.ts +2 -0
- package/dist/components/SessionRecorderWidget/index.js +1 -0
- package/dist/components/SessionRecorderWidget/index.js.map +1 -0
- package/dist/components/SessionRecorderWidget/styles.d.ts +165 -0
- package/dist/components/SessionRecorderWidget/styles.js +1 -0
- package/dist/components/SessionRecorderWidget/styles.js.map +1 -0
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/config/constants.js +1 -1
- package/dist/config/constants.js.map +1 -1
- package/dist/config/defaults.d.ts +4 -4
- package/dist/config/defaults.js +1 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/masking.d.ts +2 -2
- package/dist/config/masking.js +1 -1
- package/dist/config/masking.js.map +1 -1
- package/dist/config/session-recorder.js +1 -1
- package/dist/config/session-recorder.js.map +1 -1
- package/dist/config/validators.d.ts +1 -1
- package/dist/config/validators.js +1 -1
- package/dist/config/validators.js.map +1 -1
- package/dist/config/widget.d.ts +9 -0
- package/dist/config/widget.js +1 -0
- package/dist/config/widget.js.map +1 -0
- package/dist/context/SessionRecorderContext.d.ts +12 -3
- package/dist/context/SessionRecorderContext.js +1 -1
- package/dist/context/SessionRecorderContext.js.map +1 -1
- package/dist/context/SessionRecorderStore.d.ts +12 -0
- package/dist/context/SessionRecorderStore.js +1 -0
- package/dist/context/SessionRecorderStore.js.map +1 -0
- package/dist/context/useSessionRecorderStore.d.ts +8 -0
- package/dist/context/useSessionRecorderStore.js +1 -0
- package/dist/context/useSessionRecorderStore.js.map +1 -0
- package/dist/context/useStoreSelector.d.ts +4 -0
- package/dist/context/useStoreSelector.js +1 -0
- package/dist/context/useStoreSelector.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/native/GestureRecorderNative.d.ts +57 -0
- package/dist/native/GestureRecorderNative.js +1 -0
- package/dist/native/GestureRecorderNative.js.map +1 -0
- package/dist/native/SessionRecorderNative.d.ts +33 -0
- package/dist/native/SessionRecorderNative.js +1 -0
- package/dist/native/SessionRecorderNative.js.map +1 -0
- package/dist/native/index.d.ts +2 -0
- package/dist/native/index.js +1 -0
- package/dist/native/index.js.map +1 -0
- package/dist/otel/index.js +1 -1
- package/dist/otel/index.js.map +1 -1
- package/dist/patch/xhr.js +1 -1
- package/dist/patch/xhr.js.map +1 -1
- package/dist/recorder/eventExporter.d.ts +4 -1
- package/dist/recorder/eventExporter.js +1 -1
- package/dist/recorder/eventExporter.js.map +1 -1
- package/dist/recorder/gestureRecorder.d.ts +28 -62
- package/dist/recorder/gestureRecorder.js +1 -1
- package/dist/recorder/gestureRecorder.js.map +1 -1
- package/dist/recorder/index.d.ts +2 -0
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.js.map +1 -1
- package/dist/recorder/navigationTracker.d.ts +4 -19
- package/dist/recorder/navigationTracker.js +1 -1
- package/dist/recorder/navigationTracker.js.map +1 -1
- package/dist/recorder/screenRecorder.d.ts +11 -5
- package/dist/recorder/screenRecorder.js +1 -1
- package/dist/recorder/screenRecorder.js.map +1 -1
- package/dist/services/api.service.d.ts +12 -3
- package/dist/services/api.service.js +1 -1
- package/dist/services/api.service.js.map +1 -1
- package/dist/services/network.service.d.ts +46 -0
- package/dist/services/network.service.js +1 -0
- package/dist/services/network.service.js.map +1 -0
- package/dist/services/screenMaskingService.d.ts +47 -0
- package/dist/services/screenMaskingService.js +1 -0
- package/dist/services/screenMaskingService.js.map +1 -0
- package/dist/services/storage.service.d.ts +18 -2
- package/dist/services/storage.service.js +1 -1
- package/dist/services/storage.service.js.map +1 -1
- package/dist/session-recorder.d.ts +18 -33
- package/dist/session-recorder.js +1 -1
- package/dist/session-recorder.js.map +1 -1
- package/dist/types/configs.d.ts +85 -0
- package/dist/types/configs.js +1 -0
- package/dist/types/configs.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/session-recorder.d.ts +105 -132
- package/dist/types/session-recorder.js +1 -1
- package/dist/types/session-recorder.js.map +1 -1
- package/dist/utils/constants.optional.d.ts +21 -0
- package/dist/utils/constants.optional.expo.d.ts +3 -0
- package/dist/utils/constants.optional.expo.js +1 -0
- package/dist/utils/constants.optional.expo.js.map +1 -0
- package/dist/utils/constants.optional.js +1 -0
- package/dist/utils/constants.optional.js.map +1 -0
- package/dist/utils/createStore.d.ts +8 -0
- package/dist/utils/createStore.js +1 -0
- package/dist/utils/createStore.js.map +1 -0
- package/dist/utils/logger.d.ts +2 -7
- package/dist/utils/logger.js +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +1 -1
- package/dist/utils/platform.js.map +1 -1
- package/dist/utils/rrweb-events.d.ts +4 -3
- package/dist/utils/rrweb-events.js +1 -1
- package/dist/utils/rrweb-events.js.map +1 -1
- package/dist/utils/session.d.ts +2 -1
- package/dist/utils/session.js +1 -1
- package/dist/utils/session.js.map +1 -1
- package/dist/utils/shallowEqual.d.ts +1 -0
- package/dist/utils/shallowEqual.js +1 -0
- package/dist/utils/shallowEqual.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/ios/GestureRecorderNative.m +21 -0
- package/ios/GestureRecorderNative.swift +316 -0
- package/ios/SessionRecorderNative.m +17 -0
- package/ios/SessionRecorderNative.podspec +26 -0
- package/ios/SessionRecorderNative.swift +599 -0
- package/package.json +15 -16
- package/react-native.config.js +12 -0
- package/RRWEB_INTEGRATION.md +0 -336
- package/VIEWSHOT_INTEGRATION_TEST.md +0 -123
- package/babel.config.js +0 -13
- package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.d.ts +0 -6
- package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js +0 -1
- package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js.map +0 -1
- package/dist/components/GestureCaptureWrapper/index.d.ts +0 -1
- package/dist/components/GestureCaptureWrapper/index.js +0 -1
- package/dist/components/GestureCaptureWrapper/index.js.map +0 -1
- package/dist/components/GestureCaptureWrapper.d.ts +0 -6
- package/dist/components/GestureCaptureWrapper.js +0 -1
- package/dist/components/GestureCaptureWrapper.js.map +0 -1
- package/dist/expo.d.ts +0 -7
- package/dist/expo.js +0 -1
- package/dist/expo.js.map +0 -1
- package/dist/otel/instrumentations/gestureInstrumentation.d.ts +0 -15
- package/dist/otel/instrumentations/gestureInstrumentation.js +0 -1
- package/dist/otel/instrumentations/gestureInstrumentation.js.map +0 -1
- package/dist/otel/instrumentations/reactNativeInstrumentation.d.ts +0 -8
- package/dist/otel/instrumentations/reactNativeInstrumentation.js +0 -1
- package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +0 -1
- package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +0 -13
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js +0 -1
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +0 -1
- package/dist/recorder/gestureHandlerRecorder.d.ts +0 -19
- package/dist/recorder/gestureHandlerRecorder.js +0 -1
- package/dist/recorder/gestureHandlerRecorder.js.map +0 -1
- package/dist/types/rrweb.d.ts +0 -118
- package/dist/types/rrweb.js +0 -1
- package/dist/types/rrweb.js.map +0 -1
- 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/index.ts +0 -1
- package/src/config/constants.ts +0 -60
- package/src/config/defaults.ts +0 -82
- package/src/config/index.ts +0 -6
- package/src/config/masking.ts +0 -27
- package/src/config/session-recorder.ts +0 -55
- package/src/config/validators.ts +0 -31
- package/src/context/SessionRecorderContext.tsx +0 -75
- package/src/expo.ts +0 -11
- package/src/index.ts +0 -17
- 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 -142
- 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 -498
- package/src/services/api.service.ts +0 -203
- package/src/services/storage.service.ts +0 -158
- package/src/session-recorder.ts +0 -600
- package/src/types/expo.d.ts +0 -23
- package/src/types/index.ts +0 -28
- package/src/types/session-recorder.ts +0 -423
- 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/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
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
import { GestureEvent, RecorderConfig, EventRecorder } from '../types'
|
|
2
|
-
import { trace, SpanStatusCode } from '@opentelemetry/api'
|
|
3
|
-
import { Dimensions } from 'react-native'
|
|
4
|
-
import { logger } from '../utils'
|
|
5
|
-
import { MouseInteractions, eventWithTime, EventType, IncrementalSource } from '@rrweb/types'
|
|
6
|
-
|
|
7
|
-
export class GestureRecorder implements EventRecorder {
|
|
8
|
-
private config?: RecorderConfig
|
|
9
|
-
private isRecording = false
|
|
10
|
-
private events: GestureEvent[] = []
|
|
11
|
-
private gestureHandlers: Map<string, any> = new Map()
|
|
12
|
-
private screenDimensions: { width: number; height: number } | null = null
|
|
13
|
-
private lastGestureTime: number = 0
|
|
14
|
-
private gestureThrottleMs: number = 50 // Throttle gestures to avoid spam
|
|
15
|
-
private lastTouchTime: number = 0
|
|
16
|
-
private touchThrottleMs: number = 100 // Throttle touch events to max 10 per second
|
|
17
|
-
|
|
18
|
-
// Cyclic call detection
|
|
19
|
-
private isRecordingGesture = false
|
|
20
|
-
private gestureCallStack: string[] = []
|
|
21
|
-
private maxGestureCallDepth = 5
|
|
22
|
-
private eventRecorder?: EventRecorder
|
|
23
|
-
private imageNodeId: number = 1 // ID of the image node for touch interactions
|
|
24
|
-
private screenRecorder?: any // Reference to screen recorder for force capture
|
|
25
|
-
|
|
26
|
-
init(config: RecorderConfig, eventRecorder?: EventRecorder, screenRecorder?: any): void {
|
|
27
|
-
this.config = config
|
|
28
|
-
this.eventRecorder = eventRecorder
|
|
29
|
-
this.screenRecorder = screenRecorder
|
|
30
|
-
this._getScreenDimensions()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
start(): void {
|
|
34
|
-
logger.info('GestureRecorder', 'Gesture recording started')
|
|
35
|
-
this.isRecording = true
|
|
36
|
-
this.events = []
|
|
37
|
-
// Gesture recording started
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
stop(): void {
|
|
41
|
-
this.isRecording = false
|
|
42
|
-
this._removeGestureHandlers()
|
|
43
|
-
// Gesture recording stopped
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
private _getScreenDimensions(): void {
|
|
48
|
-
try {
|
|
49
|
-
this.screenDimensions = Dimensions.get('window')
|
|
50
|
-
} catch (error) {
|
|
51
|
-
// Failed to get screen dimensions - silently continue
|
|
52
|
-
this.screenDimensions = { width: 375, height: 667 } // Default fallback
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
private _removeGestureHandlers(): void {
|
|
58
|
-
this.gestureHandlers.clear()
|
|
59
|
-
// Gesture handlers removed
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private _recordEvent(event: GestureEvent): void {
|
|
63
|
-
if (!this.isRecording) return
|
|
64
|
-
|
|
65
|
-
// Throttle gestures to avoid spam
|
|
66
|
-
const now = Date.now()
|
|
67
|
-
if (now - this.lastGestureTime < this.gestureThrottleMs) {
|
|
68
|
-
return
|
|
69
|
-
}
|
|
70
|
-
this.lastGestureTime = now
|
|
71
|
-
|
|
72
|
-
this.events.push(event)
|
|
73
|
-
this._sendEvent(event)
|
|
74
|
-
this._recordOpenTelemetrySpan(event)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
private _sendEvent(event: GestureEvent): void {
|
|
80
|
-
// Send event to backend or store locally
|
|
81
|
-
// Gesture event recorded
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
private _recordOpenTelemetrySpan(event: GestureEvent): void {
|
|
85
|
-
try {
|
|
86
|
-
const span = trace.getTracer('@opentelemetry/instrumentation-user-interaction').startSpan(`Gesture.${event.type}`, {
|
|
87
|
-
attributes: {
|
|
88
|
-
'gesture.type': event.type,
|
|
89
|
-
'gesture.timestamp': event.timestamp,
|
|
90
|
-
'gesture.platform': 'react-native',
|
|
91
|
-
},
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
if (event.coordinates) {
|
|
95
|
-
span.setAttribute('gesture.coordinates.x', event.coordinates.x)
|
|
96
|
-
span.setAttribute('gesture.coordinates.y', event.coordinates.y)
|
|
97
|
-
|
|
98
|
-
// Calculate relative position
|
|
99
|
-
if (this.screenDimensions) {
|
|
100
|
-
const relativeX = event.coordinates.x / this.screenDimensions.width
|
|
101
|
-
const relativeY = event.coordinates.y / this.screenDimensions.height
|
|
102
|
-
span.setAttribute('gesture.coordinates.relative_x', relativeX)
|
|
103
|
-
span.setAttribute('gesture.coordinates.relative_y', relativeY)
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (event.target) {
|
|
108
|
-
span.setAttribute('gesture.target', event.target)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (event.metadata) {
|
|
112
|
-
Object.entries(event.metadata).forEach(([key, value]) => {
|
|
113
|
-
span.setAttribute(`gesture.metadata.${key}`, String(value))
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
span.setStatus({ code: SpanStatusCode.OK })
|
|
118
|
-
span.end()
|
|
119
|
-
} catch (error) {
|
|
120
|
-
// Failed to record OpenTelemetry span for gesture - silently continue
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Public methods for manual event recording
|
|
125
|
-
recordTap(x: number, y: number, target?: string, pressure?: number): void {
|
|
126
|
-
const event: GestureEvent = {
|
|
127
|
-
type: 'tap',
|
|
128
|
-
timestamp: Date.now(),
|
|
129
|
-
coordinates: { x, y },
|
|
130
|
-
target,
|
|
131
|
-
metadata: {
|
|
132
|
-
pressure: pressure || 1.0,
|
|
133
|
-
screenWidth: this.screenDimensions?.width,
|
|
134
|
-
screenHeight: this.screenDimensions?.height,
|
|
135
|
-
},
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
this._recordEvent(event)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
recordSwipe(direction: string, target?: string, velocity?: number, distance?: number): void {
|
|
142
|
-
const event: GestureEvent = {
|
|
143
|
-
type: 'swipe',
|
|
144
|
-
timestamp: Date.now(),
|
|
145
|
-
target,
|
|
146
|
-
metadata: {
|
|
147
|
-
direction,
|
|
148
|
-
velocity: velocity || 0,
|
|
149
|
-
distance: distance || 0,
|
|
150
|
-
screenWidth: this.screenDimensions?.width,
|
|
151
|
-
screenHeight: this.screenDimensions?.height,
|
|
152
|
-
},
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
this._recordEvent(event)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
recordPinch(scale: number, target?: string, velocity?: number): void {
|
|
159
|
-
const event: GestureEvent = {
|
|
160
|
-
type: 'pinch',
|
|
161
|
-
timestamp: Date.now(),
|
|
162
|
-
target,
|
|
163
|
-
metadata: {
|
|
164
|
-
scale,
|
|
165
|
-
velocity: velocity || 0,
|
|
166
|
-
screenWidth: this.screenDimensions?.width,
|
|
167
|
-
screenHeight: this.screenDimensions?.height,
|
|
168
|
-
},
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
this._recordEvent(event)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
recordPan(deltaX: number, deltaY: number, target?: string, velocity?: number): void {
|
|
175
|
-
const event: GestureEvent = {
|
|
176
|
-
type: 'pan',
|
|
177
|
-
timestamp: Date.now(),
|
|
178
|
-
target,
|
|
179
|
-
metadata: {
|
|
180
|
-
deltaX,
|
|
181
|
-
deltaY,
|
|
182
|
-
velocity: velocity || 0,
|
|
183
|
-
screenWidth: this.screenDimensions?.width,
|
|
184
|
-
screenHeight: this.screenDimensions?.height,
|
|
185
|
-
},
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
this._recordEvent(event)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
recordLongPress(duration: number, target?: string, pressure?: number): void {
|
|
192
|
-
const event: GestureEvent = {
|
|
193
|
-
type: 'longPress',
|
|
194
|
-
timestamp: Date.now(),
|
|
195
|
-
target,
|
|
196
|
-
metadata: {
|
|
197
|
-
duration,
|
|
198
|
-
pressure: pressure || 1.0,
|
|
199
|
-
screenWidth: this.screenDimensions?.width,
|
|
200
|
-
screenHeight: this.screenDimensions?.height,
|
|
201
|
-
},
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
this._recordEvent(event)
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
recordDoubleTap(x: number, y: number, target?: string): void {
|
|
208
|
-
const event: GestureEvent = {
|
|
209
|
-
type: 'doubleTap',
|
|
210
|
-
timestamp: Date.now(),
|
|
211
|
-
coordinates: { x, y },
|
|
212
|
-
target,
|
|
213
|
-
metadata: {
|
|
214
|
-
screenWidth: this.screenDimensions?.width,
|
|
215
|
-
screenHeight: this.screenDimensions?.height,
|
|
216
|
-
},
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
this._recordEvent(event)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
recordRotate(rotation: number, target?: string, velocity?: number): void {
|
|
223
|
-
const event: GestureEvent = {
|
|
224
|
-
type: 'rotate',
|
|
225
|
-
timestamp: Date.now(),
|
|
226
|
-
target,
|
|
227
|
-
metadata: {
|
|
228
|
-
rotation,
|
|
229
|
-
velocity: velocity || 0,
|
|
230
|
-
screenWidth: this.screenDimensions?.width,
|
|
231
|
-
screenHeight: this.screenDimensions?.height,
|
|
232
|
-
},
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
this._recordEvent(event)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
recordFling(direction: string, velocity: number, target?: string): void {
|
|
239
|
-
const event: GestureEvent = {
|
|
240
|
-
type: 'fling',
|
|
241
|
-
timestamp: Date.now(),
|
|
242
|
-
target,
|
|
243
|
-
metadata: {
|
|
244
|
-
direction,
|
|
245
|
-
velocity,
|
|
246
|
-
screenWidth: this.screenDimensions?.width,
|
|
247
|
-
screenHeight: this.screenDimensions?.height,
|
|
248
|
-
},
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
this._recordEvent(event)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Advanced gesture tracking methods
|
|
255
|
-
recordMultiTouch(touchCount: number, target?: string): void {
|
|
256
|
-
const event: GestureEvent = {
|
|
257
|
-
type: 'multiTouch',
|
|
258
|
-
timestamp: Date.now(),
|
|
259
|
-
target,
|
|
260
|
-
metadata: {
|
|
261
|
-
touchCount,
|
|
262
|
-
screenWidth: this.screenDimensions?.width,
|
|
263
|
-
screenHeight: this.screenDimensions?.height,
|
|
264
|
-
},
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
this._recordEvent(event)
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
recordScroll(direction: string, distance: number, velocity: number, target?: string): void {
|
|
271
|
-
const event: GestureEvent = {
|
|
272
|
-
type: 'scroll',
|
|
273
|
-
timestamp: Date.now(),
|
|
274
|
-
target,
|
|
275
|
-
metadata: {
|
|
276
|
-
direction,
|
|
277
|
-
distance,
|
|
278
|
-
velocity,
|
|
279
|
-
screenWidth: this.screenDimensions?.width,
|
|
280
|
-
screenHeight: this.screenDimensions?.height,
|
|
281
|
-
},
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
this._recordEvent(event)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
recordZoom(scale: number, target?: string, velocity?: number): void {
|
|
288
|
-
const event: GestureEvent = {
|
|
289
|
-
type: 'zoom',
|
|
290
|
-
timestamp: Date.now(),
|
|
291
|
-
target,
|
|
292
|
-
metadata: {
|
|
293
|
-
scale,
|
|
294
|
-
velocity: velocity || 0,
|
|
295
|
-
screenWidth: this.screenDimensions?.width,
|
|
296
|
-
screenHeight: this.screenDimensions?.height,
|
|
297
|
-
},
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
this._recordEvent(event)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Get recorded events
|
|
304
|
-
getEvents(): GestureEvent[] {
|
|
305
|
-
return [...this.events]
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Clear events
|
|
309
|
-
clearEvents(): void {
|
|
310
|
-
this.events = []
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Get event statistics
|
|
314
|
-
getEventStats(): Record<string, number> {
|
|
315
|
-
const stats: Record<string, number> = {}
|
|
316
|
-
this.events.forEach(event => {
|
|
317
|
-
stats[event.type] = (stats[event.type] || 0) + 1
|
|
318
|
-
})
|
|
319
|
-
return stats
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Set gesture throttle
|
|
323
|
-
setGestureThrottle(throttleMs: number): void {
|
|
324
|
-
this.gestureThrottleMs = throttleMs
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Get recording status
|
|
328
|
-
isRecordingEnabled(): boolean {
|
|
329
|
-
return this.isRecording
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Record an rrweb event
|
|
334
|
-
* @param event - The rrweb event to record
|
|
335
|
-
*/
|
|
336
|
-
recordEvent(event: any): void {
|
|
337
|
-
if (this.eventRecorder) {
|
|
338
|
-
this.eventRecorder.recordEvent(event)
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Create and emit a rrweb MouseInteraction event for touch interactions
|
|
344
|
-
* @param x - X coordinate
|
|
345
|
-
* @param y - Y coordinate
|
|
346
|
-
* @param interactionType - Type of interaction (TouchStart, TouchMove, TouchEnd, etc.)
|
|
347
|
-
* @param target - Target element identifier
|
|
348
|
-
*/
|
|
349
|
-
private _createMouseInteractionEvent(
|
|
350
|
-
x: number,
|
|
351
|
-
y: number,
|
|
352
|
-
interactionType: MouseInteractions,
|
|
353
|
-
target?: string,
|
|
354
|
-
): void {
|
|
355
|
-
const incrementalSnapshotEvent: eventWithTime = {
|
|
356
|
-
type: EventType.IncrementalSnapshot,
|
|
357
|
-
data: {
|
|
358
|
-
source: IncrementalSource.MouseInteraction,
|
|
359
|
-
type: interactionType,
|
|
360
|
-
id: this.imageNodeId, // Reference to the image node
|
|
361
|
-
x: x, // Preserve decimal precision like web rrweb
|
|
362
|
-
y: y, // Preserve decimal precision like web rrweb
|
|
363
|
-
pointerType: 2, // 2 = Touch for React Native (0=Mouse, 1=Pen, 2=Touch)
|
|
364
|
-
},
|
|
365
|
-
timestamp: Date.now(),
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
this.recordEvent(incrementalSnapshotEvent)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Create mouse move event with positions array (like web rrweb)
|
|
373
|
-
* @param x - X coordinate
|
|
374
|
-
* @param y - Y coordinate
|
|
375
|
-
* @param target - Target element identifier
|
|
376
|
-
*/
|
|
377
|
-
private _createMouseMoveEvent(x: number, y: number, target?: string): void {
|
|
378
|
-
const incrementalSnapshotEvent: eventWithTime = {
|
|
379
|
-
type: EventType.IncrementalSnapshot,
|
|
380
|
-
data: {
|
|
381
|
-
source: IncrementalSource.TouchMove, // Use MouseMove instead of MouseInteraction
|
|
382
|
-
positions: [
|
|
383
|
-
{
|
|
384
|
-
x: x, // Preserve decimal precision like web rrweb
|
|
385
|
-
y: y, // Preserve decimal precision like web rrweb
|
|
386
|
-
id: this.imageNodeId, // Reference to the image node
|
|
387
|
-
timeOffset: 0, // No time offset for single position
|
|
388
|
-
},
|
|
389
|
-
],
|
|
390
|
-
},
|
|
391
|
-
timestamp: Date.now(),
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
this.recordEvent(incrementalSnapshotEvent)
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Record touch start event as rrweb MouseInteraction
|
|
399
|
-
* @param x - X coordinate
|
|
400
|
-
* @param y - Y coordinate
|
|
401
|
-
* @param target - Target element identifier
|
|
402
|
-
* @param pressure - Touch pressure (optional)
|
|
403
|
-
*/
|
|
404
|
-
recordTouchStart(x: number, y: number, target?: string, pressure?: number): void {
|
|
405
|
-
// Throttle touch events to prevent spam
|
|
406
|
-
const now = Date.now()
|
|
407
|
-
if (now - this.lastTouchTime < this.touchThrottleMs) {
|
|
408
|
-
logger.debug('GestureRecorder', `Touch start throttled (${now - this.lastTouchTime}ms < ${this.touchThrottleMs}ms)`)
|
|
409
|
-
return
|
|
410
|
-
}
|
|
411
|
-
this.lastTouchTime = now
|
|
412
|
-
|
|
413
|
-
logger.debug('GestureRecorder', 'Touch start recorded', { x, y, target, pressure })
|
|
414
|
-
// Record as MouseDown (type: 1) like web rrweb
|
|
415
|
-
this._createMouseInteractionEvent(x, y, MouseInteractions.TouchStart, target)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Record touch move event as rrweb MouseMove with positions array
|
|
420
|
-
* @param x - X coordinate
|
|
421
|
-
* @param y - Y coordinate
|
|
422
|
-
* @param target - Target element identifier
|
|
423
|
-
* @param pressure - Touch pressure (optional)
|
|
424
|
-
*/
|
|
425
|
-
recordTouchMove(x: number, y: number, target?: string, pressure?: number): void {
|
|
426
|
-
// Throttle touch move events more aggressively
|
|
427
|
-
const now = Date.now()
|
|
428
|
-
if (now - this.lastTouchTime < this.touchThrottleMs * 2) { // 200ms throttle for move events
|
|
429
|
-
logger.debug('GestureRecorder', `Touch move throttled (${now - this.lastTouchTime}ms < ${this.touchThrottleMs * 2}ms)`)
|
|
430
|
-
return
|
|
431
|
-
}
|
|
432
|
-
this.lastTouchTime = now
|
|
433
|
-
|
|
434
|
-
logger.debug('GestureRecorder', 'Touch move recorded', { x, y, target, pressure })
|
|
435
|
-
// Record as MouseMove with positions array (like web rrweb)
|
|
436
|
-
this._createMouseMoveEvent(x, y, target)
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* Record touch end event as rrweb MouseInteraction
|
|
441
|
-
* @param x - X coordinate
|
|
442
|
-
* @param y - Y coordinate
|
|
443
|
-
* @param target - Target element identifier
|
|
444
|
-
* @param pressure - Touch pressure (optional)
|
|
445
|
-
*/
|
|
446
|
-
recordTouchEnd(x: number, y: number, target?: string, pressure?: number): void {
|
|
447
|
-
// Cyclic call detection
|
|
448
|
-
if (this.isRecordingGesture) {
|
|
449
|
-
logger.error('GestureRecorder', 'CYCLIC CALL DETECTED! Already recording gesture', this.gestureCallStack)
|
|
450
|
-
return
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (this.gestureCallStack.length >= this.maxGestureCallDepth) {
|
|
454
|
-
logger.error('GestureRecorder', 'MAX GESTURE CALL DEPTH REACHED!', this.gestureCallStack)
|
|
455
|
-
return
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
this.isRecordingGesture = true
|
|
459
|
-
this.gestureCallStack.push('recordTouchEnd')
|
|
460
|
-
|
|
461
|
-
try {
|
|
462
|
-
logger.debug('GestureRecorder', 'Touch end recorded', { x, y, target, pressure })
|
|
463
|
-
// Always record touch end (no throttling for completion)
|
|
464
|
-
this.recordTap(x, y, target, pressure)
|
|
465
|
-
// Record as MouseUp (type: 0) like web rrweb
|
|
466
|
-
this._createMouseInteractionEvent(x, y, MouseInteractions.TouchEnd, target)
|
|
467
|
-
// Also record Click (type: 2) like web rrweb
|
|
468
|
-
// this._createMouseInteractionEvent(x, y, MouseInteractions.Click, target)
|
|
469
|
-
|
|
470
|
-
// Only force screen capture on touch end (not on every touch event)
|
|
471
|
-
logger.debug('GestureRecorder', 'Forcing screen capture after touch end')
|
|
472
|
-
this.screenRecorder?.forceCapture()
|
|
473
|
-
} finally {
|
|
474
|
-
this.isRecordingGesture = false
|
|
475
|
-
this.gestureCallStack.pop()
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* Record touch cancel event as rrweb MouseInteraction
|
|
481
|
-
* @param x - X coordinate
|
|
482
|
-
* @param y - Y coordinate
|
|
483
|
-
* @param target - Target element identifier
|
|
484
|
-
*/
|
|
485
|
-
recordTouchCancel(x: number, y: number, target?: string): void {
|
|
486
|
-
// Record as MouseUp (type: 0) like web rrweb for touch cancel
|
|
487
|
-
this._createMouseInteractionEvent(x, y, MouseInteractions.TouchCancel, target)
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
/**
|
|
491
|
-
* Set the image node ID for touch interactions
|
|
492
|
-
* This should be called when a new screen snapshot is created
|
|
493
|
-
* @param nodeId - The ID of the image node in the current snapshot
|
|
494
|
-
*/
|
|
495
|
-
setImageNodeId(nodeId: number): void {
|
|
496
|
-
this.imageNodeId = nodeId
|
|
497
|
-
}
|
|
498
|
-
}
|
package/src/recorder/index.ts
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { SessionType } from '@multiplayer-app/session-recorder-common'
|
|
2
|
-
// import { pack } from '@rrweb/packer' // Removed to avoid blob creation issues in Hermes
|
|
3
|
-
import { EventExporter } from './eventExporter'
|
|
4
|
-
import { logger } from '../utils'
|
|
5
|
-
import { ScreenRecorder } from './screenRecorder'
|
|
6
|
-
import { GestureRecorder } from './gestureRecorder'
|
|
7
|
-
import { NavigationTracker } from './navigationTracker'
|
|
8
|
-
import { RecorderConfig, EventRecorder } from '../types'
|
|
9
|
-
import { eventWithTime } from '@rrweb/types'
|
|
10
|
-
export class RecorderReactNativeSDK implements EventRecorder {
|
|
11
|
-
private isRecording = false
|
|
12
|
-
private config?: RecorderConfig
|
|
13
|
-
private screenRecorder: ScreenRecorder
|
|
14
|
-
private gestureRecorder: GestureRecorder
|
|
15
|
-
private navigationTracker: NavigationTracker
|
|
16
|
-
private recordedEvents: eventWithTime[] = []
|
|
17
|
-
private exporter: EventExporter | undefined
|
|
18
|
-
private sessionId: string | null = null
|
|
19
|
-
private sessionType: SessionType = SessionType.PLAIN
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
constructor() {
|
|
23
|
-
this.screenRecorder = new ScreenRecorder()
|
|
24
|
-
this.gestureRecorder = new GestureRecorder()
|
|
25
|
-
this.navigationTracker = new NavigationTracker()
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
init(config: RecorderConfig): void {
|
|
29
|
-
this.config = config
|
|
30
|
-
this.gestureRecorder.init(config, this, this.screenRecorder)
|
|
31
|
-
this.navigationTracker.init(config)
|
|
32
|
-
this.screenRecorder.init(config, this)
|
|
33
|
-
this.exporter = new EventExporter({
|
|
34
|
-
socketUrl: config.apiBaseUrl || '',
|
|
35
|
-
apiKey: config.apiKey,
|
|
36
|
-
})
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
start(sessionId: string | null, sessionType: SessionType): void {
|
|
40
|
-
if (!this.config) {
|
|
41
|
-
throw new Error('Configuration not initialized. Call init() before start().')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
this.sessionId = sessionId
|
|
45
|
-
this.sessionType = sessionType
|
|
46
|
-
this.isRecording = true
|
|
47
|
-
|
|
48
|
-
// Emit recording started meta event
|
|
49
|
-
|
|
50
|
-
if (this.config.recordScreen) {
|
|
51
|
-
this.screenRecorder.start()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (this.config.recordGestures) {
|
|
55
|
-
this.gestureRecorder.start()
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (this.config.recordNavigation) {
|
|
59
|
-
this.navigationTracker.start()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
stop(): void {
|
|
66
|
-
this.isRecording = false
|
|
67
|
-
this.gestureRecorder.stop()
|
|
68
|
-
this.navigationTracker.stop()
|
|
69
|
-
this.screenRecorder.stop()
|
|
70
|
-
this.exporter?.close()
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
setNavigationRef(ref: any): void {
|
|
75
|
-
this.navigationTracker.setNavigationRef(ref)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Set the viewshot ref for screen capture
|
|
80
|
-
* @param ref - React Native View ref for screen capture
|
|
81
|
-
*/
|
|
82
|
-
setViewShotRef(ref: any): void {
|
|
83
|
-
this.screenRecorder.setViewShotRef(ref)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Record an rrweb event
|
|
88
|
-
* @param event - The rrweb event to record
|
|
89
|
-
*/
|
|
90
|
-
recordEvent(event: eventWithTime): void {
|
|
91
|
-
if (!this.isRecording) {
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (this.exporter) {
|
|
96
|
-
logger.debug('RecorderReactNativeSDK', 'Sending to exporter', event)
|
|
97
|
-
// Skip packing to avoid blob creation issues in Hermes
|
|
98
|
-
// const packedEvent = pack(event)
|
|
99
|
-
this.exporter.send({
|
|
100
|
-
event: event, // Send raw event instead of packed
|
|
101
|
-
eventType: event.type,
|
|
102
|
-
timestamp: event.timestamp,
|
|
103
|
-
debugSessionId: this.sessionId,
|
|
104
|
-
debugSessionType: this.sessionType,
|
|
105
|
-
})
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Record touch start event
|
|
111
|
-
* @param x - X coordinate
|
|
112
|
-
* @param y - Y coordinate
|
|
113
|
-
* @param target - Target element identifier
|
|
114
|
-
* @param pressure - Touch pressure
|
|
115
|
-
*/
|
|
116
|
-
recordTouchStart(x: number, y: number, target?: string, pressure?: number): void {
|
|
117
|
-
if (!this.isRecording) {
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
this.gestureRecorder.recordTouchStart(x, y, target, pressure)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Record touch move event
|
|
126
|
-
* @param x - X coordinate
|
|
127
|
-
* @param y - Y coordinate
|
|
128
|
-
* @param target - Target element identifier
|
|
129
|
-
* @param pressure - Touch pressure
|
|
130
|
-
*/
|
|
131
|
-
recordTouchMove(x: number, y: number, target?: string, pressure?: number): void {
|
|
132
|
-
if (!this.isRecording) {
|
|
133
|
-
return
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
this.gestureRecorder.recordTouchMove(x, y, target, pressure)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Record touch end event
|
|
141
|
-
* @param x - X coordinate
|
|
142
|
-
* @param y - Y coordinate
|
|
143
|
-
* @param target - Target element identifier
|
|
144
|
-
* @param pressure - Touch pressure
|
|
145
|
-
*/
|
|
146
|
-
recordTouchEnd(x: number, y: number, target?: string, pressure?: number): void {
|
|
147
|
-
if (!this.isRecording) {
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
this.gestureRecorder.recordTouchEnd(x, y, target, pressure)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Get all recorded events
|
|
156
|
-
* @returns Array of recorded rrweb events
|
|
157
|
-
*/
|
|
158
|
-
getRecordedEvents(): eventWithTime[] {
|
|
159
|
-
return [...this.recordedEvents]
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Clear all recorded events
|
|
164
|
-
*/
|
|
165
|
-
clearRecordedEvents(): void {
|
|
166
|
-
this.recordedEvents = []
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Get recording statistics
|
|
171
|
-
* @returns Recording statistics
|
|
172
|
-
*/
|
|
173
|
-
getRecordingStats(): { totalEvents: number; isRecording: boolean } {
|
|
174
|
-
return {
|
|
175
|
-
totalEvents: this.recordedEvents.length,
|
|
176
|
-
isRecording: this.isRecording,
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|