@multiplayer-app/session-recorder-react-native 0.0.1-alpha.6 → 0.0.1-alpha.7
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/RRWEB_INTEGRATION.md +336 -0
- package/VIEWSHOT_INTEGRATION_TEST.md +123 -0
- package/copy-react-native-dist.sh +38 -0
- package/dist/config/constants.d.ts +0 -1
- package/dist/config/constants.js +1 -1
- package/dist/config/constants.js.map +1 -1
- package/dist/context/SessionRecorderContext.js +1 -1
- package/dist/context/SessionRecorderContext.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/otel/helpers.d.ts +4 -4
- package/dist/otel/helpers.js +1 -1
- package/dist/otel/helpers.js.map +1 -1
- package/dist/otel/index.js +1 -1
- package/dist/otel/index.js.map +1 -1
- package/dist/otel/instrumentations/index.d.ts +2 -3
- package/dist/otel/instrumentations/index.js +1 -1
- package/dist/otel/instrumentations/index.js.map +1 -1
- package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +1 -0
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js +1 -1
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +1 -1
- package/dist/recorder/eventExporter.d.ts +21 -0
- package/dist/recorder/eventExporter.js +1 -0
- package/dist/recorder/eventExporter.js.map +1 -0
- package/dist/recorder/gestureRecorder.d.ts +57 -3
- package/dist/recorder/gestureRecorder.js +1 -1
- package/dist/recorder/gestureRecorder.js.map +1 -1
- package/dist/recorder/index.d.ts +61 -7
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.js.map +1 -1
- package/dist/recorder/screenRecorder.d.ts +58 -4
- package/dist/recorder/screenRecorder.js +1 -1
- package/dist/recorder/screenRecorder.js.map +1 -1
- package/dist/services/api.service.js.map +1 -1
- package/dist/services/storage.service.js.map +1 -1
- package/dist/session-recorder.d.ts +40 -2
- package/dist/session-recorder.js +1 -1
- package/dist/session-recorder.js.map +1 -1
- 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/rrweb.d.ts +108 -0
- package/dist/types/rrweb.js +1 -0
- package/dist/types/rrweb.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/example-usage.tsx +174 -0
- package/package.json +3 -2
- package/src/config/constants.ts +3 -3
- package/src/context/SessionRecorderContext.tsx +93 -16
- package/src/index.ts +1 -0
- package/src/otel/helpers.ts +37 -20
- package/src/otel/index.ts +7 -3
- package/src/otel/instrumentations/index.ts +79 -38
- package/src/otel/instrumentations/reactNavigationInstrumentation.ts +5 -0
- package/src/recorder/eventExporter.ts +138 -0
- package/src/recorder/gestureRecorder.ts +124 -3
- package/src/recorder/index.ts +130 -21
- package/src/recorder/screenRecorder.ts +203 -7
- package/src/services/api.service.ts +1 -8
- package/src/services/storage.service.ts +1 -0
- package/src/session-recorder.ts +91 -12
- package/src/types/index.ts +2 -1
- package/src/types/rrweb.ts +122 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import io, { Socket } from 'socket.io-client'
|
|
2
|
+
|
|
3
|
+
import { ISession } from '../types'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
SESSION_ADD_EVENT,
|
|
7
|
+
SESSION_AUTO_CREATED,
|
|
8
|
+
SESSION_STOPPED_EVENT,
|
|
9
|
+
SESSION_SUBSCRIBE_EVENT,
|
|
10
|
+
SESSION_UNSUBSCRIBE_EVENT,
|
|
11
|
+
} from '../config'
|
|
12
|
+
|
|
13
|
+
const MAX_RECONNECTION_ATTEMPTS = 2
|
|
14
|
+
|
|
15
|
+
export class EventExporter {
|
|
16
|
+
private socket: Socket | null = null
|
|
17
|
+
private queue: any[] = []
|
|
18
|
+
private isConnecting: boolean = false
|
|
19
|
+
private isConnected: boolean = false
|
|
20
|
+
private attempts: number = 0
|
|
21
|
+
private sessionId: string | null = null
|
|
22
|
+
|
|
23
|
+
constructor(private options: { socketUrl: string, apiKey: string }) { }
|
|
24
|
+
|
|
25
|
+
private init(): void {
|
|
26
|
+
if (this.isConnecting || this.isConnected) return
|
|
27
|
+
this.attempts++
|
|
28
|
+
this.isConnecting = true
|
|
29
|
+
this.socket = io(this.options.socketUrl, {
|
|
30
|
+
path: '/v0/radar/ws',
|
|
31
|
+
auth: {
|
|
32
|
+
'x-api-key': this.options.apiKey,
|
|
33
|
+
},
|
|
34
|
+
reconnectionAttempts: 2,
|
|
35
|
+
transports: ['websocket'],
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// this.socket.on('connect', () => {
|
|
39
|
+
// this.isConnecting = false
|
|
40
|
+
// this.isConnected = true
|
|
41
|
+
// this.usePostMessage = false
|
|
42
|
+
// this.flushQueue()
|
|
43
|
+
// })
|
|
44
|
+
|
|
45
|
+
this.socket.on('ready', () => {
|
|
46
|
+
this.isConnecting = false
|
|
47
|
+
this.isConnected = true
|
|
48
|
+
|
|
49
|
+
this.flushQueue()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
this.socket.on('disconnect', (err: any) => {
|
|
53
|
+
this.isConnecting = false
|
|
54
|
+
this.isConnected = false
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
this.socket.on('connect_error', (err: any) => {
|
|
58
|
+
this.isConnecting = false
|
|
59
|
+
this.isConnected = false
|
|
60
|
+
this.checkReconnectionAttempts()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
this.socket.on(SESSION_STOPPED_EVENT, (data: any) => {
|
|
64
|
+
|
|
65
|
+
this.unsubscribeFromSession()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
this.socket.on(SESSION_AUTO_CREATED, (data: any) => {
|
|
69
|
+
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private checkReconnectionAttempts(): void {
|
|
74
|
+
if (this.attempts >= MAX_RECONNECTION_ATTEMPTS) {
|
|
75
|
+
|
|
76
|
+
this.flushQueue()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
private flushQueue(): void {
|
|
82
|
+
while (this.queue.length > 0 && (this.socket?.connected)) {
|
|
83
|
+
const event = this.queue.shift()
|
|
84
|
+
if (!event) continue
|
|
85
|
+
|
|
86
|
+
if (this.socket?.connected) {
|
|
87
|
+
this.socket.emit(event.name, event.data)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private unsubscribeFromSession() {
|
|
93
|
+
const payload = {
|
|
94
|
+
debugSessionId: this.sessionId,
|
|
95
|
+
}
|
|
96
|
+
if (this.socket?.connected) {
|
|
97
|
+
this.socket.emit(SESSION_UNSUBSCRIBE_EVENT, payload)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public send(event: any): void {
|
|
102
|
+
if (this.socket?.connected) {
|
|
103
|
+
this.socket.emit(SESSION_ADD_EVENT, event)
|
|
104
|
+
} else {
|
|
105
|
+
this.queue.push({ data: event, name: SESSION_ADD_EVENT })
|
|
106
|
+
this.init()
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public subscribeToSession(session: ISession): void {
|
|
111
|
+
this.sessionId = session.shortId || session._id
|
|
112
|
+
const payload = {
|
|
113
|
+
projectId: session.project,
|
|
114
|
+
workspaceId: session.workspace,
|
|
115
|
+
debugSessionId: this.sessionId,
|
|
116
|
+
sessionType: session.creationType,
|
|
117
|
+
}
|
|
118
|
+
if (this.socket?.connected) {
|
|
119
|
+
this.socket.emit(SESSION_SUBSCRIBE_EVENT, payload)
|
|
120
|
+
} else {
|
|
121
|
+
this.queue.push({ data: payload, name: SESSION_SUBSCRIBE_EVENT })
|
|
122
|
+
this.init()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public close(): void {
|
|
127
|
+
if (this.socket?.connected) {
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
this.unsubscribeFromSession()
|
|
130
|
+
this.attempts = 0
|
|
131
|
+
this.isConnected = false
|
|
132
|
+
this.isConnecting = false
|
|
133
|
+
this.socket?.disconnect()
|
|
134
|
+
this.socket = null
|
|
135
|
+
}, 500)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { GestureEvent, RecorderConfig } from '../types'
|
|
1
|
+
import { GestureEvent, RecorderConfig, EventType, IncrementalSource, IncrementalSnapshotEvent, MouseInteractionType, EventRecorder } from '../types'
|
|
2
2
|
import { trace, SpanStatusCode } from '@opentelemetry/api'
|
|
3
3
|
import { Dimensions } from 'react-native'
|
|
4
4
|
|
|
5
|
-
export class GestureRecorder {
|
|
5
|
+
export class GestureRecorder implements EventRecorder {
|
|
6
6
|
private config?: RecorderConfig
|
|
7
7
|
private isRecording = false
|
|
8
8
|
private events: GestureEvent[] = []
|
|
@@ -10,8 +10,13 @@ export class GestureRecorder {
|
|
|
10
10
|
private screenDimensions: { width: number; height: number } | null = null
|
|
11
11
|
private lastGestureTime: number = 0
|
|
12
12
|
private gestureThrottleMs: number = 50 // Throttle gestures to avoid spam
|
|
13
|
-
|
|
13
|
+
private eventRecorder?: EventRecorder
|
|
14
|
+
private imageNodeId: number = 1 // ID of the image node for touch interactions
|
|
15
|
+
private screenRecorder?: any // Reference to screen recorder for force capture
|
|
16
|
+
init(config: RecorderConfig, eventRecorder?: EventRecorder, screenRecorder?: any): void {
|
|
14
17
|
this.config = config
|
|
18
|
+
this.eventRecorder = eventRecorder
|
|
19
|
+
this.screenRecorder = screenRecorder
|
|
15
20
|
this._getScreenDimensions()
|
|
16
21
|
}
|
|
17
22
|
|
|
@@ -19,6 +24,7 @@ export class GestureRecorder {
|
|
|
19
24
|
this.isRecording = true
|
|
20
25
|
this.events = []
|
|
21
26
|
this._setupGestureHandlers()
|
|
27
|
+
this._setupAutomaticTouchCapture()
|
|
22
28
|
// Gesture recording started
|
|
23
29
|
}
|
|
24
30
|
|
|
@@ -77,6 +83,19 @@ export class GestureRecorder {
|
|
|
77
83
|
}
|
|
78
84
|
}
|
|
79
85
|
|
|
86
|
+
private _setupAutomaticTouchCapture(): void {
|
|
87
|
+
try {
|
|
88
|
+
// This method sets up automatic touch capture
|
|
89
|
+
// The actual touch capture is handled by the TouchEventCapture component
|
|
90
|
+
// in the SessionRecorderContext, which automatically calls our recording methods
|
|
91
|
+
|
|
92
|
+
// We can add any additional setup here if needed
|
|
93
|
+
// For now, the TouchEventCapture component handles everything automatically
|
|
94
|
+
} catch (error) {
|
|
95
|
+
// Failed to setup automatic touch capture - silently continue
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
80
99
|
private _removeGestureHandlers(): void {
|
|
81
100
|
this.gestureHandlers.clear()
|
|
82
101
|
// Gesture handlers removed
|
|
@@ -424,4 +443,106 @@ export class GestureRecorder {
|
|
|
424
443
|
isRecordingEnabled(): boolean {
|
|
425
444
|
return this.isRecording
|
|
426
445
|
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Record an rrweb event
|
|
449
|
+
* @param event - The rrweb event to record
|
|
450
|
+
*/
|
|
451
|
+
recordEvent(event: any): void {
|
|
452
|
+
if (this.eventRecorder) {
|
|
453
|
+
this.eventRecorder.recordEvent(event)
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Create and emit a rrweb MouseInteraction event for touch interactions
|
|
459
|
+
* @param x - X coordinate
|
|
460
|
+
* @param y - Y coordinate
|
|
461
|
+
* @param interactionType - Type of interaction (TouchStart, TouchMove, TouchEnd, etc.)
|
|
462
|
+
* @param target - Target element identifier
|
|
463
|
+
*/
|
|
464
|
+
private _createMouseInteractionEvent(
|
|
465
|
+
x: number,
|
|
466
|
+
y: number,
|
|
467
|
+
interactionType: MouseInteractionType,
|
|
468
|
+
target?: string,
|
|
469
|
+
): void {
|
|
470
|
+
const incrementalSnapshotEvent: IncrementalSnapshotEvent = {
|
|
471
|
+
type: EventType.IncrementalSnapshot,
|
|
472
|
+
data: {
|
|
473
|
+
source: IncrementalSource.MouseInteraction,
|
|
474
|
+
type: interactionType,
|
|
475
|
+
id: this.imageNodeId, // Reference to the image node
|
|
476
|
+
x: Math.round(x),
|
|
477
|
+
y: Math.round(y),
|
|
478
|
+
},
|
|
479
|
+
timestamp: Date.now(),
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
this.recordEvent(incrementalSnapshotEvent)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Record touch start event as rrweb MouseInteraction
|
|
487
|
+
* @param x - X coordinate
|
|
488
|
+
* @param y - Y coordinate
|
|
489
|
+
* @param target - Target element identifier
|
|
490
|
+
* @param pressure - Touch pressure (optional)
|
|
491
|
+
*/
|
|
492
|
+
recordTouchStart(x: number, y: number, target?: string, pressure?: number): void {
|
|
493
|
+
// Record as both gesture event and rrweb event
|
|
494
|
+
this.recordTap(x, y, target, pressure)
|
|
495
|
+
this._createMouseInteractionEvent(x, y, MouseInteractionType.TouchStart, target)
|
|
496
|
+
|
|
497
|
+
// Force screen capture after touch interaction
|
|
498
|
+
this.screenRecorder?.forceCapture()
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Record touch move event as rrweb MouseInteraction
|
|
503
|
+
* @param x - X coordinate
|
|
504
|
+
* @param y - Y coordinate
|
|
505
|
+
* @param target - Target element identifier
|
|
506
|
+
* @param pressure - Touch pressure (optional)
|
|
507
|
+
*/
|
|
508
|
+
recordTouchMove(x: number, y: number, target?: string, pressure?: number): void {
|
|
509
|
+
// Record as both gesture event and rrweb event
|
|
510
|
+
this.recordPan(x - (this.lastGestureTime || 0), y - (this.lastGestureTime || 0), target)
|
|
511
|
+
this._createMouseInteractionEvent(x, y, MouseInteractionType.TouchMove, target)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Record touch end event as rrweb MouseInteraction
|
|
516
|
+
* @param x - X coordinate
|
|
517
|
+
* @param y - Y coordinate
|
|
518
|
+
* @param target - Target element identifier
|
|
519
|
+
* @param pressure - Touch pressure (optional)
|
|
520
|
+
*/
|
|
521
|
+
recordTouchEnd(x: number, y: number, target?: string, pressure?: number): void {
|
|
522
|
+
// Record as both gesture event and rrweb event
|
|
523
|
+
this.recordTap(x, y, target, pressure)
|
|
524
|
+
this._createMouseInteractionEvent(x, y, MouseInteractionType.TouchEnd, target)
|
|
525
|
+
|
|
526
|
+
// Force screen capture after touch interaction
|
|
527
|
+
this.screenRecorder?.forceCapture()
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Record touch cancel event as rrweb MouseInteraction
|
|
532
|
+
* @param x - X coordinate
|
|
533
|
+
* @param y - Y coordinate
|
|
534
|
+
* @param target - Target element identifier
|
|
535
|
+
*/
|
|
536
|
+
recordTouchCancel(x: number, y: number, target?: string): void {
|
|
537
|
+
this._createMouseInteractionEvent(x, y, MouseInteractionType.TouchCancel, target)
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Set the image node ID for touch interactions
|
|
542
|
+
* This should be called when a new screen snapshot is created
|
|
543
|
+
* @param nodeId - The ID of the image node in the current snapshot
|
|
544
|
+
*/
|
|
545
|
+
setImageNodeId(nodeId: number): void {
|
|
546
|
+
this.imageNodeId = nodeId
|
|
547
|
+
}
|
|
427
548
|
}
|
package/src/recorder/index.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { NavigationTracker } from './navigationTracker'
|
|
3
|
-
import { ScreenRecorder } from './screenRecorder'
|
|
1
|
+
import { pack } from '@rrweb/packer'
|
|
4
2
|
import { SessionType } from '@multiplayer-app/session-recorder-common'
|
|
5
|
-
import { RecorderConfig } from '../types'
|
|
6
3
|
|
|
7
|
-
|
|
4
|
+
import { EventExporter } from './eventExporter'
|
|
5
|
+
import { ScreenRecorder } from './screenRecorder'
|
|
6
|
+
import { GestureRecorder } from './gestureRecorder'
|
|
7
|
+
import { NavigationTracker } from './navigationTracker'
|
|
8
|
+
import { RecorderConfig, EventRecorder, RRWebEvent } from '../types'
|
|
9
|
+
export class RecorderReactNativeSDK implements EventRecorder {
|
|
10
|
+
private isRecording = false
|
|
8
11
|
private config?: RecorderConfig
|
|
12
|
+
private eventRecorder?: EventRecorder
|
|
13
|
+
private screenRecorder: ScreenRecorder
|
|
9
14
|
private gestureRecorder: GestureRecorder
|
|
10
15
|
private navigationTracker: NavigationTracker
|
|
11
|
-
private
|
|
12
|
-
private
|
|
16
|
+
private recordedEvents: RRWebEvent[] = []
|
|
17
|
+
private exporter: EventExporter | undefined
|
|
18
|
+
private sessionId: string | null = null
|
|
19
|
+
private sessionType: SessionType = SessionType.PLAIN
|
|
13
20
|
|
|
14
21
|
constructor() {
|
|
15
22
|
this.gestureRecorder = new GestureRecorder()
|
|
@@ -17,11 +24,16 @@ export class RecorderReactNativeSDK {
|
|
|
17
24
|
this.screenRecorder = new ScreenRecorder()
|
|
18
25
|
}
|
|
19
26
|
|
|
20
|
-
init(config: RecorderConfig): void {
|
|
27
|
+
init(config: RecorderConfig, eventRecorder?: EventRecorder): void {
|
|
21
28
|
this.config = config
|
|
22
|
-
this.
|
|
29
|
+
this.eventRecorder = eventRecorder
|
|
30
|
+
this.gestureRecorder.init(config, this, this.screenRecorder)
|
|
23
31
|
this.navigationTracker.init(config)
|
|
24
|
-
this.screenRecorder.init(config)
|
|
32
|
+
this.screenRecorder.init(config, this)
|
|
33
|
+
this.exporter = new EventExporter({
|
|
34
|
+
socketUrl: config.apiBaseUrl || '',
|
|
35
|
+
apiKey: config.apiKey,
|
|
36
|
+
})
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
start(sessionId: string | null, sessionType: SessionType): void {
|
|
@@ -29,6 +41,8 @@ export class RecorderReactNativeSDK {
|
|
|
29
41
|
throw new Error('Configuration not initialized. Call init() before start().')
|
|
30
42
|
}
|
|
31
43
|
|
|
44
|
+
this.sessionId = sessionId
|
|
45
|
+
this.sessionType = sessionType
|
|
32
46
|
this.isRecording = true
|
|
33
47
|
|
|
34
48
|
if (this.config.recordGestures) {
|
|
@@ -49,23 +63,118 @@ export class RecorderReactNativeSDK {
|
|
|
49
63
|
this.gestureRecorder.stop()
|
|
50
64
|
this.navigationTracker.stop()
|
|
51
65
|
this.screenRecorder.stop()
|
|
66
|
+
this.exporter?.close()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
setNavigationRef(ref: any): void {
|
|
71
|
+
this.navigationTracker.setNavigationRef(ref)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Set the viewshot ref for screen capture
|
|
76
|
+
* @param ref - React Native View ref for screen capture
|
|
77
|
+
*/
|
|
78
|
+
setViewShotRef(ref: any): void {
|
|
79
|
+
this.screenRecorder.setViewShotRef(ref)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Record an rrweb event
|
|
84
|
+
* @param event - The rrweb event to record
|
|
85
|
+
*/
|
|
86
|
+
recordEvent(event: RRWebEvent): void {
|
|
87
|
+
if (!this.isRecording) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Store the event locally
|
|
92
|
+
this.recordedEvents.push(event)
|
|
93
|
+
if (this.exporter) {
|
|
94
|
+
const packedEvent = pack(event)
|
|
95
|
+
this.exporter.send({
|
|
96
|
+
event: packedEvent,
|
|
97
|
+
eventType: event.type,
|
|
98
|
+
timestamp: event.timestamp,
|
|
99
|
+
debugSessionId: this.sessionId,
|
|
100
|
+
debugSessionType: this.sessionType,
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Forward to parent event recorder if available
|
|
105
|
+
if (this.eventRecorder) {
|
|
106
|
+
this.eventRecorder.recordEvent(event)
|
|
107
|
+
}
|
|
52
108
|
}
|
|
53
109
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Record touch start event
|
|
112
|
+
* @param x - X coordinate
|
|
113
|
+
* @param y - Y coordinate
|
|
114
|
+
* @param target - Target element identifier
|
|
115
|
+
* @param pressure - Touch pressure
|
|
116
|
+
*/
|
|
117
|
+
recordTouchStart(x: number, y: number, target?: string, pressure?: number): void {
|
|
118
|
+
if (!this.isRecording) {
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.gestureRecorder.recordTouchStart(x, y, target, pressure)
|
|
58
123
|
}
|
|
59
124
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Record touch move event
|
|
127
|
+
* @param x - X coordinate
|
|
128
|
+
* @param y - Y coordinate
|
|
129
|
+
* @param target - Target element identifier
|
|
130
|
+
* @param pressure - Touch pressure
|
|
131
|
+
*/
|
|
132
|
+
recordTouchMove(x: number, y: number, target?: string, pressure?: number): void {
|
|
133
|
+
if (!this.isRecording) {
|
|
134
|
+
return
|
|
65
135
|
}
|
|
136
|
+
|
|
137
|
+
this.gestureRecorder.recordTouchMove(x, y, target, pressure)
|
|
66
138
|
}
|
|
67
139
|
|
|
68
|
-
|
|
69
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Record touch end event
|
|
142
|
+
* @param x - X coordinate
|
|
143
|
+
* @param y - Y coordinate
|
|
144
|
+
* @param target - Target element identifier
|
|
145
|
+
* @param pressure - Touch pressure
|
|
146
|
+
*/
|
|
147
|
+
recordTouchEnd(x: number, y: number, target?: string, pressure?: number): void {
|
|
148
|
+
if (!this.isRecording) {
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.gestureRecorder.recordTouchEnd(x, y, target, pressure)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get all recorded events
|
|
157
|
+
* @returns Array of recorded rrweb events
|
|
158
|
+
*/
|
|
159
|
+
getRecordedEvents(): RRWebEvent[] {
|
|
160
|
+
return [...this.recordedEvents]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Clear all recorded events
|
|
165
|
+
*/
|
|
166
|
+
clearRecordedEvents(): void {
|
|
167
|
+
this.recordedEvents = []
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get recording statistics
|
|
172
|
+
* @returns Recording statistics
|
|
173
|
+
*/
|
|
174
|
+
getRecordingStats(): { totalEvents: number; isRecording: boolean } {
|
|
175
|
+
return {
|
|
176
|
+
totalEvents: this.recordedEvents.length,
|
|
177
|
+
isRecording: this.isRecording,
|
|
178
|
+
}
|
|
70
179
|
}
|
|
71
180
|
}
|