@multiplayer-app/session-recorder-react-native 1.0.1-beta.4 → 1.0.1-beta.6
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/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeConfig.kt +1 -1
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeModule.kt +9 -9
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativePackage.kt +2 -2
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/model/TargetInfo.kt +1 -1
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/util/ViewUtils.kt +2 -2
- package/lib/module/SessionRecorderNativeSpec.js +5 -0
- package/lib/module/SessionRecorderNativeSpec.js.map +1 -0
- package/lib/module/components/SessionRecorderWidget/ErrorBanner.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/ModalHeader.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/icons.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/styles.js.map +1 -1
- package/lib/module/config/constants.js.map +1 -1
- package/lib/module/config/defaults.js.map +1 -1
- package/lib/module/config/masking.js.map +1 -1
- package/lib/module/config/session-recorder.js.map +1 -1
- package/lib/module/config/validators.js.map +1 -1
- package/lib/module/config/widget.js.map +1 -1
- package/lib/module/context/SessionRecorderStore.js.map +1 -1
- package/lib/module/context/useSessionRecorderStore.js.map +1 -1
- package/lib/module/context/useStoreSelector.js.map +1 -1
- package/lib/module/native/SessionRecorderNative.js +16 -11
- package/lib/module/native/SessionRecorderNative.js.map +1 -1
- package/lib/module/native/index.js.map +1 -1
- package/lib/module/otel/helpers.js +1 -1
- package/lib/module/otel/helpers.js.map +1 -1
- package/lib/module/otel/index.js.map +1 -1
- package/lib/module/otel/instrumentations/index.js.map +1 -1
- package/lib/module/patch/xhr.js.map +1 -1
- package/lib/module/recorder/eventExporter.js.map +1 -1
- package/lib/module/recorder/gestureRecorder.js.map +1 -1
- package/lib/module/recorder/index.js.map +1 -1
- package/lib/module/recorder/navigationTracker.js.map +1 -1
- package/lib/module/recorder/screenRecorder.js.map +1 -1
- package/lib/module/services/api.service.js.map +1 -1
- package/lib/module/services/network.service.js.map +1 -1
- package/lib/module/services/screenMaskingService.js +1 -1
- package/lib/module/services/screenMaskingService.js.map +1 -1
- package/lib/module/services/storage.service.js.map +1 -1
- package/lib/module/session-recorder.js.map +1 -1
- package/lib/module/types/index.js.map +1 -1
- package/lib/module/types/session-recorder.js.map +1 -1
- package/lib/module/utils/app-metadata.js +2 -2
- package/lib/module/utils/constants.optional.js.map +1 -1
- package/lib/module/utils/createStore.js.map +1 -1
- package/lib/module/utils/logger.js +0 -8
- package/lib/module/utils/logger.js.map +1 -1
- package/lib/module/utils/platform.js +1 -1
- package/lib/module/utils/platform.js.map +1 -1
- package/lib/module/utils/rrweb-events.js.map +1 -1
- package/lib/module/utils/session.js.map +1 -1
- package/lib/module/utils/shallowEqual.js.map +1 -1
- package/lib/module/utils/time.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/SessionRecorderNativeSpec.d.ts +42 -0
- package/lib/typescript/src/SessionRecorderNativeSpec.d.ts.map +1 -0
- package/lib/typescript/src/components/ScreenRecorderView/index.d.ts +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/ErrorBanner.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/SessionRecorderWidget.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/icons.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/index.d.ts +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/config/constants.d.ts.map +1 -1
- package/lib/typescript/src/config/defaults.d.ts.map +1 -1
- package/lib/typescript/src/config/index.d.ts.map +1 -1
- package/lib/typescript/src/config/masking.d.ts.map +1 -1
- package/lib/typescript/src/config/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/config/validators.d.ts.map +1 -1
- package/lib/typescript/src/config/widget.d.ts +1 -1
- package/lib/typescript/src/config/widget.d.ts.map +1 -1
- package/lib/typescript/src/context/SessionRecorderStore.d.ts.map +1 -1
- package/lib/typescript/src/context/useSessionRecorderStore.d.ts +3 -3
- package/lib/typescript/src/context/useSessionRecorderStore.d.ts.map +1 -1
- package/lib/typescript/src/context/useStoreSelector.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/SessionRecorderNative.d.ts +3 -54
- package/lib/typescript/src/native/SessionRecorderNative.d.ts.map +1 -1
- package/lib/typescript/src/native/index.d.ts +1 -1
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/lib/typescript/src/otel/helpers.d.ts.map +1 -1
- package/lib/typescript/src/otel/index.d.ts.map +1 -1
- package/lib/typescript/src/otel/instrumentations/index.d.ts.map +1 -1
- package/lib/typescript/src/patch/index.d.ts.map +1 -1
- package/lib/typescript/src/patch/xhr.d.ts.map +1 -1
- package/lib/typescript/src/recorder/eventExporter.d.ts.map +1 -1
- package/lib/typescript/src/recorder/gestureRecorder.d.ts.map +1 -1
- package/lib/typescript/src/recorder/index.d.ts.map +1 -1
- package/lib/typescript/src/recorder/navigationTracker.d.ts.map +1 -1
- package/lib/typescript/src/recorder/screenRecorder.d.ts.map +1 -1
- package/lib/typescript/src/services/api.service.d.ts.map +1 -1
- package/lib/typescript/src/services/network.service.d.ts.map +1 -1
- package/lib/typescript/src/services/screenMaskingService.d.ts +1 -1
- package/lib/typescript/src/services/screenMaskingService.d.ts.map +1 -1
- package/lib/typescript/src/services/storage.service.d.ts.map +1 -1
- package/lib/typescript/src/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/configs.d.ts.map +1 -1
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/lib/typescript/src/types/session-recorder.d.ts +4 -4
- package/lib/typescript/src/types/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/session.d.ts.map +1 -1
- package/lib/typescript/src/utils/app-metadata.d.ts.map +1 -1
- package/lib/typescript/src/utils/constants.optional.d.ts.map +1 -1
- package/lib/typescript/src/utils/constants.optional.expo.d.ts.map +1 -1
- package/lib/typescript/src/utils/createStore.d.ts.map +1 -1
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/logger.d.ts +1 -1
- package/lib/typescript/src/utils/logger.d.ts.map +1 -1
- package/lib/typescript/src/utils/platform.d.ts.map +1 -1
- package/lib/typescript/src/utils/request-utils.d.ts.map +1 -1
- package/lib/typescript/src/utils/rrweb-events.d.ts.map +1 -1
- package/lib/typescript/src/utils/session.d.ts.map +1 -1
- package/lib/typescript/src/utils/shallowEqual.d.ts.map +1 -1
- package/lib/typescript/src/utils/time.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-utils.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/SessionRecorderNativeSpec.ts +53 -0
- package/src/components/ScreenRecorderView/index.ts +1 -1
- package/src/components/SessionRecorderWidget/ErrorBanner.tsx +14 -14
- package/src/components/SessionRecorderWidget/ModalHeader.tsx +11 -9
- package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +70 -56
- package/src/components/SessionRecorderWidget/icons.tsx +58 -30
- package/src/components/SessionRecorderWidget/index.ts +1 -1
- package/src/components/SessionRecorderWidget/styles.ts +17 -18
- package/src/components/index.ts +2 -2
- package/src/config/constants.ts +19 -20
- package/src/config/defaults.ts +35 -31
- package/src/config/index.ts +5 -5
- package/src/config/masking.ts +44 -18
- package/src/config/session-recorder.ts +54 -26
- package/src/config/validators.ts +43 -20
- package/src/config/widget.ts +24 -15
- package/src/context/SessionRecorderStore.ts +19 -18
- package/src/context/useSessionRecorderStore.ts +17 -10
- package/src/context/useStoreSelector.ts +20 -18
- package/src/index.ts +7 -7
- package/src/native/SessionRecorderNative.ts +68 -112
- package/src/native/index.ts +5 -1
- package/src/otel/helpers.ts +109 -93
- package/src/otel/index.ts +46 -49
- package/src/otel/instrumentations/index.ts +44 -41
- package/src/patch/index.ts +1 -1
- package/src/patch/xhr.ts +77 -78
- package/src/recorder/eventExporter.ts +63 -68
- package/src/recorder/gestureRecorder.ts +359 -212
- package/src/recorder/index.ts +75 -62
- package/src/recorder/navigationTracker.ts +120 -97
- package/src/recorder/screenRecorder.ts +214 -163
- package/src/services/api.service.ts +49 -48
- package/src/services/network.service.ts +67 -58
- package/src/services/screenMaskingService.ts +81 -50
- package/src/services/storage.service.ts +99 -70
- package/src/session-recorder.ts +270 -214
- package/src/types/configs.ts +53 -31
- package/src/types/expo-constants.d.ts +2 -2
- package/src/types/index.ts +16 -18
- package/src/types/session-recorder.ts +106 -111
- package/src/types/session.ts +45 -45
- package/src/utils/app-metadata.ts +9 -9
- package/src/utils/constants.optional.expo.ts +3 -3
- package/src/utils/constants.optional.ts +14 -12
- package/src/utils/createStore.ts +23 -20
- package/src/utils/index.ts +7 -7
- package/src/utils/logger.ts +87 -58
- package/src/utils/platform.ts +149 -118
- package/src/utils/request-utils.ts +15 -15
- package/src/utils/rrweb-events.ts +47 -34
- package/src/utils/session.ts +15 -12
- package/src/utils/shallowEqual.ts +16 -10
- package/src/utils/time.ts +7 -4
- package/src/utils/type-utils.ts +36 -36
- package/src/version.ts +1 -1
- package/android/src/main/java/com/multiplayer/sessionrecordernative/SessionRecorderNativeModuleSpec.kt +0 -51
- package/android/src/main/java/com/xxx/XxxModule.kt +0 -23
- package/ios/Xxx.h +0 -5
- package/ios/Xxx.mm +0 -21
|
@@ -1,117 +1,153 @@
|
|
|
1
|
-
import { Dimensions, Platform } from 'react-native'
|
|
2
|
-
import { trace, SpanStatusCode, type Span } from '@opentelemetry/api'
|
|
3
|
-
import { logger } from '../utils'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { Dimensions, Platform } from 'react-native';
|
|
2
|
+
import { trace, SpanStatusCode, type Span } from '@opentelemetry/api';
|
|
3
|
+
import { logger } from '../utils';
|
|
4
|
+
import {
|
|
5
|
+
type GestureEvent,
|
|
6
|
+
type RecorderConfig,
|
|
7
|
+
type EventRecorder,
|
|
8
|
+
} from '../types';
|
|
9
|
+
import {
|
|
10
|
+
MouseInteractions,
|
|
11
|
+
type eventWithTime,
|
|
12
|
+
EventType,
|
|
13
|
+
IncrementalSource,
|
|
14
|
+
} from '@rrweb/types';
|
|
15
|
+
import {
|
|
16
|
+
type NativeGestureEvent,
|
|
17
|
+
SessionRecorderNative,
|
|
18
|
+
gestureEventEmitter,
|
|
19
|
+
} from '../native';
|
|
7
20
|
// Force TypeScript recompilation
|
|
8
21
|
|
|
9
|
-
|
|
10
22
|
export class GestureRecorder implements EventRecorder {
|
|
11
23
|
// @ts-ignore
|
|
12
|
-
private config?: RecorderConfig
|
|
13
|
-
private isRecording = false
|
|
14
|
-
private events: GestureEvent[] = []
|
|
15
|
-
private screenDimensions: { width: number; height: number } | null = null
|
|
16
|
-
private lastGestureTime: number = 0
|
|
17
|
-
private gestureThrottleMs: number = 50 // Throttle gestures to avoid spam
|
|
18
|
-
private lastTouchTime: number = 0
|
|
19
|
-
private touchThrottleMs: number = 100 // Throttle touch events to max 10 per second
|
|
20
|
-
private eventRecorder?: EventRecorder
|
|
21
|
-
private imageNodeId: number = 1 // ID of the image node for touch interactions
|
|
22
|
-
private screenRecorder?: any // Reference to screen recorder for force capture
|
|
23
|
-
private gestureEventListener?: any // Native event listener
|
|
24
|
-
private currentPanSpan?: Span // Aggregated span for pan gesture
|
|
25
|
-
private panMoveCount: number = 0
|
|
26
|
-
|
|
27
|
-
init(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
private config?: RecorderConfig;
|
|
25
|
+
private isRecording = false;
|
|
26
|
+
private events: GestureEvent[] = [];
|
|
27
|
+
private screenDimensions: { width: number; height: number } | null = null;
|
|
28
|
+
private lastGestureTime: number = 0;
|
|
29
|
+
private gestureThrottleMs: number = 50; // Throttle gestures to avoid spam
|
|
30
|
+
private lastTouchTime: number = 0;
|
|
31
|
+
private touchThrottleMs: number = 100; // Throttle touch events to max 10 per second
|
|
32
|
+
private eventRecorder?: EventRecorder;
|
|
33
|
+
private imageNodeId: number = 1; // ID of the image node for touch interactions
|
|
34
|
+
private screenRecorder?: any; // Reference to screen recorder for force capture
|
|
35
|
+
private gestureEventListener?: any; // Native event listener
|
|
36
|
+
private currentPanSpan?: Span; // Aggregated span for pan gesture
|
|
37
|
+
private panMoveCount: number = 0;
|
|
38
|
+
|
|
39
|
+
init(
|
|
40
|
+
config: RecorderConfig,
|
|
41
|
+
eventRecorder?: EventRecorder,
|
|
42
|
+
screenRecorder?: any
|
|
43
|
+
): void {
|
|
44
|
+
this.config = config;
|
|
45
|
+
this.eventRecorder = eventRecorder;
|
|
46
|
+
this.screenRecorder = screenRecorder;
|
|
47
|
+
this._getScreenDimensions();
|
|
32
48
|
}
|
|
33
49
|
|
|
34
50
|
start(): void {
|
|
35
|
-
logger.info('GestureRecorder', 'Native gesture recording started')
|
|
36
|
-
this.isRecording = true
|
|
37
|
-
this.events = []
|
|
51
|
+
logger.info('GestureRecorder', 'Native gesture recording started');
|
|
52
|
+
this.isRecording = true;
|
|
53
|
+
this.events = [];
|
|
38
54
|
|
|
39
55
|
// Check if we're on web platform
|
|
40
56
|
if (Platform.OS === 'web') {
|
|
41
|
-
logger.warn(
|
|
42
|
-
|
|
57
|
+
logger.warn(
|
|
58
|
+
'GestureRecorder',
|
|
59
|
+
'Native gesture recording not available on web platform'
|
|
60
|
+
);
|
|
61
|
+
return;
|
|
43
62
|
}
|
|
44
63
|
|
|
45
64
|
// Start native gesture recording
|
|
46
65
|
SessionRecorderNative.startGestureRecording()
|
|
47
66
|
.then(() => {
|
|
48
|
-
logger.info(
|
|
49
|
-
|
|
67
|
+
logger.info(
|
|
68
|
+
'GestureRecorder',
|
|
69
|
+
'Native gesture recording started successfully'
|
|
70
|
+
);
|
|
71
|
+
this._setupGestureEventListener();
|
|
50
72
|
})
|
|
51
73
|
.catch((error) => {
|
|
52
|
-
logger.error(
|
|
53
|
-
|
|
74
|
+
logger.error(
|
|
75
|
+
'GestureRecorder',
|
|
76
|
+
'Failed to start native gesture recording',
|
|
77
|
+
error
|
|
78
|
+
);
|
|
79
|
+
});
|
|
54
80
|
}
|
|
55
81
|
|
|
56
82
|
stop(): void {
|
|
57
|
-
this.isRecording = false
|
|
58
|
-
this._removeGestureEventListener()
|
|
83
|
+
this.isRecording = false;
|
|
84
|
+
this._removeGestureEventListener();
|
|
59
85
|
|
|
60
86
|
// Check if we're on web platform
|
|
61
87
|
if (Platform.OS === 'web') {
|
|
62
|
-
logger.warn(
|
|
63
|
-
|
|
88
|
+
logger.warn(
|
|
89
|
+
'GestureRecorder',
|
|
90
|
+
'Native gesture recording not available on web platform'
|
|
91
|
+
);
|
|
92
|
+
return;
|
|
64
93
|
}
|
|
65
94
|
|
|
66
95
|
// Stop native gesture recording
|
|
67
96
|
SessionRecorderNative.stopGestureRecording()
|
|
68
97
|
.then(() => {
|
|
69
|
-
logger.info(
|
|
98
|
+
logger.info(
|
|
99
|
+
'GestureRecorder',
|
|
100
|
+
'Native gesture recording stopped successfully'
|
|
101
|
+
);
|
|
70
102
|
})
|
|
71
103
|
.catch((error) => {
|
|
72
|
-
logger.error(
|
|
73
|
-
|
|
104
|
+
logger.error(
|
|
105
|
+
'GestureRecorder',
|
|
106
|
+
'Failed to stop native gesture recording',
|
|
107
|
+
error
|
|
108
|
+
);
|
|
109
|
+
});
|
|
74
110
|
}
|
|
75
111
|
|
|
76
112
|
private _getScreenDimensions(): void {
|
|
77
113
|
try {
|
|
78
|
-
this.screenDimensions = Dimensions.get('window')
|
|
114
|
+
this.screenDimensions = Dimensions.get('window');
|
|
79
115
|
} catch (error) {
|
|
80
116
|
// Failed to get screen dimensions - silently continue
|
|
81
|
-
this.screenDimensions = { width: 375, height: 667 } // Default fallback
|
|
117
|
+
this.screenDimensions = { width: 375, height: 667 }; // Default fallback
|
|
82
118
|
}
|
|
83
119
|
}
|
|
84
120
|
|
|
85
121
|
private _setupGestureEventListener(): void {
|
|
86
122
|
if (!gestureEventEmitter) {
|
|
87
|
-
logger.warn('GestureRecorder', 'Gesture event emitter not available')
|
|
88
|
-
return
|
|
123
|
+
logger.warn('GestureRecorder', 'Gesture event emitter not available');
|
|
124
|
+
return;
|
|
89
125
|
}
|
|
90
126
|
|
|
91
127
|
this.gestureEventListener = gestureEventEmitter.addListener(
|
|
92
128
|
'onGestureDetected',
|
|
93
129
|
(nativeGesture: any) => {
|
|
94
|
-
this._handleNativeGesture(nativeGesture as NativeGestureEvent)
|
|
130
|
+
this._handleNativeGesture(nativeGesture as NativeGestureEvent);
|
|
95
131
|
}
|
|
96
|
-
)
|
|
132
|
+
);
|
|
97
133
|
}
|
|
98
134
|
|
|
99
135
|
private _removeGestureEventListener(): void {
|
|
100
136
|
if (this.gestureEventListener) {
|
|
101
|
-
this.gestureEventListener.remove()
|
|
102
|
-
this.gestureEventListener = undefined
|
|
137
|
+
this.gestureEventListener.remove();
|
|
138
|
+
this.gestureEventListener = undefined;
|
|
103
139
|
}
|
|
104
140
|
}
|
|
105
141
|
|
|
106
142
|
private _handleNativeGesture(nativeGesture: NativeGestureEvent): void {
|
|
107
|
-
if (!this.isRecording) return
|
|
143
|
+
if (!this.isRecording) return;
|
|
108
144
|
|
|
109
145
|
// Throttle gestures to avoid spam
|
|
110
|
-
const now = Date.now()
|
|
146
|
+
const now = Date.now();
|
|
111
147
|
if (now - this.lastGestureTime < this.gestureThrottleMs) {
|
|
112
|
-
return
|
|
148
|
+
return;
|
|
113
149
|
}
|
|
114
|
-
this.lastGestureTime = now
|
|
150
|
+
this.lastGestureTime = now;
|
|
115
151
|
|
|
116
152
|
// Convert native gesture to our format
|
|
117
153
|
const gesture: GestureEvent = {
|
|
@@ -125,58 +161,74 @@ export class GestureRecorder implements EventRecorder {
|
|
|
125
161
|
screenWidth: this.screenDimensions?.width,
|
|
126
162
|
screenHeight: this.screenDimensions?.height,
|
|
127
163
|
},
|
|
128
|
-
}
|
|
164
|
+
};
|
|
129
165
|
|
|
130
|
-
this.events.push(gesture)
|
|
131
|
-
this._sendEvent(gesture)
|
|
132
|
-
this._recordOpenTelemetrySpan(gesture)
|
|
166
|
+
this.events.push(gesture);
|
|
167
|
+
this._sendEvent(gesture);
|
|
168
|
+
this._recordOpenTelemetrySpan(gesture);
|
|
133
169
|
|
|
134
170
|
// Handle specific gesture types
|
|
135
171
|
switch (nativeGesture.type) {
|
|
136
172
|
case 'tap':
|
|
137
|
-
this._handleTapGesture(nativeGesture)
|
|
138
|
-
break
|
|
173
|
+
this._handleTapGesture(nativeGesture);
|
|
174
|
+
break;
|
|
139
175
|
case 'pan_start':
|
|
140
|
-
this._handlePanStartGesture(nativeGesture)
|
|
141
|
-
break
|
|
176
|
+
this._handlePanStartGesture(nativeGesture);
|
|
177
|
+
break;
|
|
142
178
|
case 'pan_move':
|
|
143
|
-
this._handlePanMoveGesture(nativeGesture)
|
|
144
|
-
break
|
|
179
|
+
this._handlePanMoveGesture(nativeGesture);
|
|
180
|
+
break;
|
|
145
181
|
case 'pan_end':
|
|
146
|
-
this._handlePanEndGesture(nativeGesture)
|
|
147
|
-
break
|
|
182
|
+
this._handlePanEndGesture(nativeGesture);
|
|
183
|
+
break;
|
|
148
184
|
case 'long_press':
|
|
149
|
-
this._handleLongPressGesture(nativeGesture)
|
|
150
|
-
break
|
|
185
|
+
this._handleLongPressGesture(nativeGesture);
|
|
186
|
+
break;
|
|
151
187
|
case 'pinch':
|
|
152
|
-
this._handlePinchGesture(nativeGesture)
|
|
153
|
-
break
|
|
188
|
+
this._handlePinchGesture(nativeGesture);
|
|
189
|
+
break;
|
|
154
190
|
case 'swipe':
|
|
155
|
-
this._handleSwipeGesture(nativeGesture)
|
|
156
|
-
break
|
|
191
|
+
this._handleSwipeGesture(nativeGesture);
|
|
192
|
+
break;
|
|
157
193
|
}
|
|
158
194
|
}
|
|
159
195
|
|
|
160
196
|
private _handleTapGesture(nativeGesture: NativeGestureEvent): void {
|
|
161
|
-
this.recordTouchStart(
|
|
162
|
-
|
|
197
|
+
this.recordTouchStart(
|
|
198
|
+
nativeGesture.x,
|
|
199
|
+
nativeGesture.y,
|
|
200
|
+
nativeGesture.target
|
|
201
|
+
);
|
|
202
|
+
this.recordTouchEnd(nativeGesture.x, nativeGesture.y, nativeGesture.target);
|
|
163
203
|
}
|
|
164
204
|
|
|
165
205
|
private _handlePanStartGesture(nativeGesture: NativeGestureEvent): void {
|
|
166
|
-
this.recordTouchStart(
|
|
206
|
+
this.recordTouchStart(
|
|
207
|
+
nativeGesture.x,
|
|
208
|
+
nativeGesture.y,
|
|
209
|
+
nativeGesture.target
|
|
210
|
+
);
|
|
167
211
|
}
|
|
168
212
|
|
|
169
213
|
private _handlePanMoveGesture(nativeGesture: NativeGestureEvent): void {
|
|
170
|
-
this.recordTouchMove(
|
|
214
|
+
this.recordTouchMove(
|
|
215
|
+
nativeGesture.x,
|
|
216
|
+
nativeGesture.y,
|
|
217
|
+
nativeGesture.target
|
|
218
|
+
);
|
|
171
219
|
}
|
|
172
220
|
|
|
173
221
|
private _handlePanEndGesture(nativeGesture: NativeGestureEvent): void {
|
|
174
|
-
this.recordTouchEnd(nativeGesture.x, nativeGesture.y, nativeGesture.target)
|
|
222
|
+
this.recordTouchEnd(nativeGesture.x, nativeGesture.y, nativeGesture.target);
|
|
175
223
|
}
|
|
176
224
|
|
|
177
225
|
private _handleLongPressGesture(nativeGesture: NativeGestureEvent): void {
|
|
178
|
-
this.recordTouchStart(
|
|
179
|
-
|
|
226
|
+
this.recordTouchStart(
|
|
227
|
+
nativeGesture.x,
|
|
228
|
+
nativeGesture.y,
|
|
229
|
+
nativeGesture.target
|
|
230
|
+
);
|
|
231
|
+
this.recordTouchEnd(nativeGesture.x, nativeGesture.y, nativeGesture.target);
|
|
180
232
|
}
|
|
181
233
|
|
|
182
234
|
private _handlePinchGesture(nativeGesture: NativeGestureEvent): void {
|
|
@@ -184,7 +236,7 @@ export class GestureRecorder implements EventRecorder {
|
|
|
184
236
|
nativeGesture.metadata?.scale || 1.0,
|
|
185
237
|
nativeGesture.target,
|
|
186
238
|
nativeGesture.metadata?.velocity || 0
|
|
187
|
-
)
|
|
239
|
+
);
|
|
188
240
|
}
|
|
189
241
|
|
|
190
242
|
private _handleSwipeGesture(nativeGesture: NativeGestureEvent): void {
|
|
@@ -192,12 +244,15 @@ export class GestureRecorder implements EventRecorder {
|
|
|
192
244
|
nativeGesture.metadata?.direction || 'unknown',
|
|
193
245
|
nativeGesture.target,
|
|
194
246
|
nativeGesture.metadata?.velocity || 0
|
|
195
|
-
)
|
|
247
|
+
);
|
|
196
248
|
}
|
|
197
249
|
|
|
198
250
|
private _sendEvent(event: GestureEvent): void {
|
|
199
251
|
// Send event to backend or store locally
|
|
200
|
-
logger.debug('GestureRecorder', 'Gesture event recorded', {
|
|
252
|
+
logger.debug('GestureRecorder', 'Gesture event recorded', {
|
|
253
|
+
type: event.type,
|
|
254
|
+
target: event.target,
|
|
255
|
+
});
|
|
201
256
|
}
|
|
202
257
|
|
|
203
258
|
private _recordOpenTelemetrySpan(event: GestureEvent): void {
|
|
@@ -206,67 +261,79 @@ export class GestureRecorder implements EventRecorder {
|
|
|
206
261
|
type: event.type,
|
|
207
262
|
target: event.target,
|
|
208
263
|
targetInfo: event.targetInfo,
|
|
209
|
-
hasTargetInfo: !!event.targetInfo
|
|
210
|
-
})
|
|
264
|
+
hasTargetInfo: !!event.targetInfo,
|
|
265
|
+
});
|
|
211
266
|
// Special handling to aggregate pan gestures into a single span
|
|
212
267
|
if (event.type === 'pan_start') {
|
|
213
268
|
// End any previously dangling pan span defensively
|
|
214
269
|
if (this.currentPanSpan) {
|
|
215
|
-
this.currentPanSpan.setStatus({ code: SpanStatusCode.OK })
|
|
216
|
-
this.currentPanSpan.end()
|
|
270
|
+
this.currentPanSpan.setStatus({ code: SpanStatusCode.OK });
|
|
271
|
+
this.currentPanSpan.end();
|
|
217
272
|
}
|
|
218
|
-
this.panMoveCount = 0
|
|
273
|
+
this.panMoveCount = 0;
|
|
219
274
|
const panSpan = trace
|
|
220
275
|
.getTracer('@opentelemetry/instrumentation-user-interaction')
|
|
221
276
|
.startSpan('NativeGesture.pan', {
|
|
222
277
|
startTime: event.timestamp,
|
|
223
|
-
})
|
|
278
|
+
});
|
|
224
279
|
|
|
225
|
-
panSpan.setAttribute('gesture.type', 'pan')
|
|
226
|
-
panSpan.setAttribute('gesture.timestamp', event.timestamp)
|
|
227
|
-
panSpan.setAttribute('gesture.platform', 'react-native')
|
|
228
|
-
panSpan.setAttribute('gesture.source', 'native-module')
|
|
280
|
+
panSpan.setAttribute('gesture.type', 'pan');
|
|
281
|
+
panSpan.setAttribute('gesture.timestamp', event.timestamp);
|
|
282
|
+
panSpan.setAttribute('gesture.platform', 'react-native');
|
|
283
|
+
panSpan.setAttribute('gesture.source', 'native-module');
|
|
229
284
|
|
|
230
285
|
if (event.coordinates) {
|
|
231
|
-
panSpan.setAttribute('gesture.start.x', event.coordinates.x)
|
|
232
|
-
panSpan.setAttribute('gesture.start.y', event.coordinates.y)
|
|
286
|
+
panSpan.setAttribute('gesture.start.x', event.coordinates.x);
|
|
287
|
+
panSpan.setAttribute('gesture.start.y', event.coordinates.y);
|
|
233
288
|
}
|
|
234
289
|
if (event.target) {
|
|
235
|
-
panSpan.setAttribute(
|
|
290
|
+
panSpan.setAttribute(
|
|
291
|
+
'gesture.target',
|
|
292
|
+
this._truncateText(event.target, 50)
|
|
293
|
+
);
|
|
236
294
|
}
|
|
237
295
|
// Enrich with target info if provided
|
|
238
|
-
const info = event.targetInfo
|
|
296
|
+
const info = event.targetInfo;
|
|
239
297
|
if (info) {
|
|
240
298
|
if (info.label) {
|
|
241
|
-
const truncatedLabel = this._truncateText(String(info.label), 50)
|
|
242
|
-
panSpan.setAttribute('gesture.target.label', truncatedLabel)
|
|
299
|
+
const truncatedLabel = this._truncateText(String(info.label), 50);
|
|
300
|
+
panSpan.setAttribute('gesture.target.label', truncatedLabel);
|
|
243
301
|
}
|
|
244
302
|
if (info.role) {
|
|
245
|
-
panSpan.setAttribute('gesture.target.role', String(info.role))
|
|
303
|
+
panSpan.setAttribute('gesture.target.role', String(info.role));
|
|
246
304
|
}
|
|
247
305
|
if (info.testId) {
|
|
248
|
-
panSpan.setAttribute('gesture.target.test_id', String(info.testId))
|
|
306
|
+
panSpan.setAttribute('gesture.target.test_id', String(info.testId));
|
|
249
307
|
}
|
|
250
308
|
if (info.text) {
|
|
251
|
-
const truncatedText = this._truncateText(String(info.text), 50)
|
|
252
|
-
panSpan.setAttribute('gesture.target.text', truncatedText)
|
|
309
|
+
const truncatedText = this._truncateText(String(info.text), 50);
|
|
310
|
+
panSpan.setAttribute('gesture.target.text', truncatedText);
|
|
253
311
|
}
|
|
254
312
|
}
|
|
255
313
|
// Save the span for subsequent move/end events
|
|
256
|
-
this.currentPanSpan = panSpan
|
|
257
|
-
return
|
|
314
|
+
this.currentPanSpan = panSpan;
|
|
315
|
+
return;
|
|
258
316
|
}
|
|
259
317
|
|
|
260
318
|
if (event.type === 'pan_move') {
|
|
261
319
|
if (this.currentPanSpan) {
|
|
262
|
-
this.panMoveCount += 1
|
|
263
|
-
this.currentPanSpan.setAttribute(
|
|
320
|
+
this.panMoveCount += 1;
|
|
321
|
+
this.currentPanSpan.setAttribute(
|
|
322
|
+
'gesture.pan.move_count',
|
|
323
|
+
this.panMoveCount
|
|
324
|
+
);
|
|
264
325
|
if (event.coordinates) {
|
|
265
|
-
this.currentPanSpan.setAttribute(
|
|
266
|
-
|
|
326
|
+
this.currentPanSpan.setAttribute(
|
|
327
|
+
'gesture.last.x',
|
|
328
|
+
event.coordinates.x
|
|
329
|
+
);
|
|
330
|
+
this.currentPanSpan.setAttribute(
|
|
331
|
+
'gesture.last.y',
|
|
332
|
+
event.coordinates.y
|
|
333
|
+
);
|
|
267
334
|
}
|
|
268
335
|
// Don't end the span here; just update it
|
|
269
|
-
return
|
|
336
|
+
return;
|
|
270
337
|
}
|
|
271
338
|
// If we received a move without a start, fall through to single-shot span below
|
|
272
339
|
}
|
|
@@ -274,14 +341,20 @@ export class GestureRecorder implements EventRecorder {
|
|
|
274
341
|
if (event.type === 'pan_end') {
|
|
275
342
|
if (this.currentPanSpan) {
|
|
276
343
|
if (event.coordinates) {
|
|
277
|
-
this.currentPanSpan.setAttribute(
|
|
278
|
-
|
|
344
|
+
this.currentPanSpan.setAttribute(
|
|
345
|
+
'gesture.end.x',
|
|
346
|
+
event.coordinates.x
|
|
347
|
+
);
|
|
348
|
+
this.currentPanSpan.setAttribute(
|
|
349
|
+
'gesture.end.y',
|
|
350
|
+
event.coordinates.y
|
|
351
|
+
);
|
|
279
352
|
}
|
|
280
|
-
this.currentPanSpan.setStatus({ code: SpanStatusCode.OK })
|
|
281
|
-
this.currentPanSpan.end()
|
|
282
|
-
this.currentPanSpan = undefined
|
|
283
|
-
this.panMoveCount = 0
|
|
284
|
-
return
|
|
353
|
+
this.currentPanSpan.setStatus({ code: SpanStatusCode.OK });
|
|
354
|
+
this.currentPanSpan.end();
|
|
355
|
+
this.currentPanSpan = undefined;
|
|
356
|
+
this.panMoveCount = 0;
|
|
357
|
+
return;
|
|
285
358
|
}
|
|
286
359
|
// If no current span, fall through and create a single-shot span for the end event
|
|
287
360
|
}
|
|
@@ -297,60 +370,76 @@ export class GestureRecorder implements EventRecorder {
|
|
|
297
370
|
'gesture.platform': 'react-native',
|
|
298
371
|
'gesture.source': 'native-module',
|
|
299
372
|
},
|
|
300
|
-
})
|
|
373
|
+
});
|
|
301
374
|
|
|
302
375
|
if (event.coordinates) {
|
|
303
|
-
span.setAttribute('gesture.coordinates.x', event.coordinates.x)
|
|
304
|
-
span.setAttribute('gesture.coordinates.y', event.coordinates.y)
|
|
376
|
+
span.setAttribute('gesture.coordinates.x', event.coordinates.x);
|
|
377
|
+
span.setAttribute('gesture.coordinates.y', event.coordinates.y);
|
|
305
378
|
|
|
306
379
|
// Calculate relative position
|
|
307
380
|
if (this.screenDimensions) {
|
|
308
|
-
const relativeX = event.coordinates.x / this.screenDimensions.width
|
|
309
|
-
const relativeY = event.coordinates.y / this.screenDimensions.height
|
|
310
|
-
span.setAttribute('gesture.coordinates.relative_x', relativeX)
|
|
311
|
-
span.setAttribute('gesture.coordinates.relative_y', relativeY)
|
|
381
|
+
const relativeX = event.coordinates.x / this.screenDimensions.width;
|
|
382
|
+
const relativeY = event.coordinates.y / this.screenDimensions.height;
|
|
383
|
+
span.setAttribute('gesture.coordinates.relative_x', relativeX);
|
|
384
|
+
span.setAttribute('gesture.coordinates.relative_y', relativeY);
|
|
312
385
|
}
|
|
313
386
|
}
|
|
314
387
|
|
|
315
388
|
if (event.target) {
|
|
316
|
-
span.setAttribute(
|
|
389
|
+
span.setAttribute(
|
|
390
|
+
'gesture.target',
|
|
391
|
+
this._truncateText(event.target, 100)
|
|
392
|
+
);
|
|
317
393
|
}
|
|
318
394
|
|
|
319
395
|
// Enrich with target info if provided
|
|
320
|
-
const info = event.targetInfo
|
|
396
|
+
const info = event.targetInfo;
|
|
321
397
|
if (info) {
|
|
322
398
|
if (info.label) {
|
|
323
|
-
const truncatedLabel = this._truncateText(String(info.label), 100)
|
|
324
|
-
span.setAttribute('gesture.target.label', truncatedLabel)
|
|
399
|
+
const truncatedLabel = this._truncateText(String(info.label), 100);
|
|
400
|
+
span.setAttribute('gesture.target.label', truncatedLabel);
|
|
325
401
|
}
|
|
326
402
|
if (info.role) {
|
|
327
|
-
span.setAttribute('gesture.target.role', String(info.role))
|
|
403
|
+
span.setAttribute('gesture.target.role', String(info.role));
|
|
328
404
|
}
|
|
329
405
|
if (info.testId) {
|
|
330
|
-
span.setAttribute('gesture.target.test_id', String(info.testId))
|
|
406
|
+
span.setAttribute('gesture.target.test_id', String(info.testId));
|
|
331
407
|
}
|
|
332
408
|
if (info.text) {
|
|
333
|
-
const truncatedText = this._truncateText(String(info.text), 200)
|
|
334
|
-
span.setAttribute('gesture.target.text', truncatedText)
|
|
409
|
+
const truncatedText = this._truncateText(String(info.text), 200);
|
|
410
|
+
span.setAttribute('gesture.target.text', truncatedText);
|
|
335
411
|
}
|
|
336
412
|
}
|
|
337
413
|
|
|
338
414
|
if (event.metadata) {
|
|
339
415
|
Object.entries(event.metadata).forEach(([key, value]) => {
|
|
340
|
-
span.setAttribute(`gesture.metadata.${key}`, String(value))
|
|
341
|
-
})
|
|
416
|
+
span.setAttribute(`gesture.metadata.${key}`, String(value));
|
|
417
|
+
});
|
|
342
418
|
}
|
|
343
419
|
|
|
344
|
-
span.setStatus({ code: SpanStatusCode.OK })
|
|
345
|
-
span.end()
|
|
346
|
-
logger.debug(
|
|
420
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
421
|
+
span.end();
|
|
422
|
+
logger.debug(
|
|
423
|
+
'GestureRecorder',
|
|
424
|
+
'OTEL span created and ended successfully'
|
|
425
|
+
);
|
|
347
426
|
} catch (error) {
|
|
348
|
-
logger.error(
|
|
427
|
+
logger.error(
|
|
428
|
+
'GestureRecorder',
|
|
429
|
+
'Failed to record OpenTelemetry span for native gesture',
|
|
430
|
+
error
|
|
431
|
+
);
|
|
349
432
|
}
|
|
350
433
|
}
|
|
351
434
|
|
|
352
435
|
// Public methods for manual event recording (same as before)
|
|
353
|
-
recordTap(
|
|
436
|
+
recordTap(
|
|
437
|
+
x: number,
|
|
438
|
+
y: number,
|
|
439
|
+
target?: string,
|
|
440
|
+
pressure?: number,
|
|
441
|
+
timestamp?: number
|
|
442
|
+
): void {
|
|
354
443
|
const event: GestureEvent = {
|
|
355
444
|
type: 'tap',
|
|
356
445
|
timestamp: timestamp || Date.now(),
|
|
@@ -361,12 +450,17 @@ export class GestureRecorder implements EventRecorder {
|
|
|
361
450
|
screenWidth: this.screenDimensions?.width,
|
|
362
451
|
screenHeight: this.screenDimensions?.height,
|
|
363
452
|
},
|
|
364
|
-
}
|
|
453
|
+
};
|
|
365
454
|
|
|
366
|
-
this._recordEvent(event)
|
|
455
|
+
this._recordEvent(event);
|
|
367
456
|
}
|
|
368
457
|
|
|
369
|
-
recordSwipe(
|
|
458
|
+
recordSwipe(
|
|
459
|
+
direction: string,
|
|
460
|
+
target?: string,
|
|
461
|
+
velocity?: number,
|
|
462
|
+
distance?: number
|
|
463
|
+
): void {
|
|
370
464
|
const event: GestureEvent = {
|
|
371
465
|
type: 'swipe',
|
|
372
466
|
timestamp: Date.now(),
|
|
@@ -378,9 +472,9 @@ export class GestureRecorder implements EventRecorder {
|
|
|
378
472
|
screenWidth: this.screenDimensions?.width,
|
|
379
473
|
screenHeight: this.screenDimensions?.height,
|
|
380
474
|
},
|
|
381
|
-
}
|
|
475
|
+
};
|
|
382
476
|
|
|
383
|
-
this._recordEvent(event)
|
|
477
|
+
this._recordEvent(event);
|
|
384
478
|
}
|
|
385
479
|
|
|
386
480
|
recordPinch(scale: number, target?: string, velocity?: number): void {
|
|
@@ -394,12 +488,17 @@ export class GestureRecorder implements EventRecorder {
|
|
|
394
488
|
screenWidth: this.screenDimensions?.width,
|
|
395
489
|
screenHeight: this.screenDimensions?.height,
|
|
396
490
|
},
|
|
397
|
-
}
|
|
491
|
+
};
|
|
398
492
|
|
|
399
|
-
this._recordEvent(event)
|
|
493
|
+
this._recordEvent(event);
|
|
400
494
|
}
|
|
401
495
|
|
|
402
|
-
recordPan(
|
|
496
|
+
recordPan(
|
|
497
|
+
deltaX: number,
|
|
498
|
+
deltaY: number,
|
|
499
|
+
target?: string,
|
|
500
|
+
velocity?: number
|
|
501
|
+
): void {
|
|
403
502
|
const event: GestureEvent = {
|
|
404
503
|
type: 'pan',
|
|
405
504
|
timestamp: Date.now(),
|
|
@@ -411,9 +510,9 @@ export class GestureRecorder implements EventRecorder {
|
|
|
411
510
|
screenWidth: this.screenDimensions?.width,
|
|
412
511
|
screenHeight: this.screenDimensions?.height,
|
|
413
512
|
},
|
|
414
|
-
}
|
|
513
|
+
};
|
|
415
514
|
|
|
416
|
-
this._recordEvent(event)
|
|
515
|
+
this._recordEvent(event);
|
|
417
516
|
}
|
|
418
517
|
|
|
419
518
|
recordLongPress(duration: number, target?: string, pressure?: number): void {
|
|
@@ -427,9 +526,9 @@ export class GestureRecorder implements EventRecorder {
|
|
|
427
526
|
screenWidth: this.screenDimensions?.width,
|
|
428
527
|
screenHeight: this.screenDimensions?.height,
|
|
429
528
|
},
|
|
430
|
-
}
|
|
529
|
+
};
|
|
431
530
|
|
|
432
|
-
this._recordEvent(event)
|
|
531
|
+
this._recordEvent(event);
|
|
433
532
|
}
|
|
434
533
|
|
|
435
534
|
recordDoubleTap(x: number, y: number, target?: string): void {
|
|
@@ -442,9 +541,9 @@ export class GestureRecorder implements EventRecorder {
|
|
|
442
541
|
screenWidth: this.screenDimensions?.width,
|
|
443
542
|
screenHeight: this.screenDimensions?.height,
|
|
444
543
|
},
|
|
445
|
-
}
|
|
544
|
+
};
|
|
446
545
|
|
|
447
|
-
this._recordEvent(event)
|
|
546
|
+
this._recordEvent(event);
|
|
448
547
|
}
|
|
449
548
|
|
|
450
549
|
recordRotate(rotation: number, target?: string, velocity?: number): void {
|
|
@@ -458,9 +557,9 @@ export class GestureRecorder implements EventRecorder {
|
|
|
458
557
|
screenWidth: this.screenDimensions?.width,
|
|
459
558
|
screenHeight: this.screenDimensions?.height,
|
|
460
559
|
},
|
|
461
|
-
}
|
|
560
|
+
};
|
|
462
561
|
|
|
463
|
-
this._recordEvent(event)
|
|
562
|
+
this._recordEvent(event);
|
|
464
563
|
}
|
|
465
564
|
|
|
466
565
|
recordFling(direction: string, velocity: number, target?: string): void {
|
|
@@ -474,9 +573,9 @@ export class GestureRecorder implements EventRecorder {
|
|
|
474
573
|
screenWidth: this.screenDimensions?.width,
|
|
475
574
|
screenHeight: this.screenDimensions?.height,
|
|
476
575
|
},
|
|
477
|
-
}
|
|
576
|
+
};
|
|
478
577
|
|
|
479
|
-
this._recordEvent(event)
|
|
578
|
+
this._recordEvent(event);
|
|
480
579
|
}
|
|
481
580
|
|
|
482
581
|
recordMultiTouch(touchCount: number, target?: string): void {
|
|
@@ -489,12 +588,17 @@ export class GestureRecorder implements EventRecorder {
|
|
|
489
588
|
screenWidth: this.screenDimensions?.width,
|
|
490
589
|
screenHeight: this.screenDimensions?.height,
|
|
491
590
|
},
|
|
492
|
-
}
|
|
591
|
+
};
|
|
493
592
|
|
|
494
|
-
this._recordEvent(event)
|
|
593
|
+
this._recordEvent(event);
|
|
495
594
|
}
|
|
496
595
|
|
|
497
|
-
recordScroll(
|
|
596
|
+
recordScroll(
|
|
597
|
+
direction: string,
|
|
598
|
+
distance: number,
|
|
599
|
+
velocity: number,
|
|
600
|
+
target?: string
|
|
601
|
+
): void {
|
|
498
602
|
const event: GestureEvent = {
|
|
499
603
|
type: 'scroll',
|
|
500
604
|
timestamp: Date.now(),
|
|
@@ -506,9 +610,9 @@ export class GestureRecorder implements EventRecorder {
|
|
|
506
610
|
screenWidth: this.screenDimensions?.width,
|
|
507
611
|
screenHeight: this.screenDimensions?.height,
|
|
508
612
|
},
|
|
509
|
-
}
|
|
613
|
+
};
|
|
510
614
|
|
|
511
|
-
this._recordEvent(event)
|
|
615
|
+
this._recordEvent(event);
|
|
512
616
|
}
|
|
513
617
|
|
|
514
618
|
recordZoom(scale: number, target?: string, velocity?: number): void {
|
|
@@ -522,78 +626,121 @@ export class GestureRecorder implements EventRecorder {
|
|
|
522
626
|
screenWidth: this.screenDimensions?.width,
|
|
523
627
|
screenHeight: this.screenDimensions?.height,
|
|
524
628
|
},
|
|
525
|
-
}
|
|
629
|
+
};
|
|
526
630
|
|
|
527
|
-
this._recordEvent(event)
|
|
631
|
+
this._recordEvent(event);
|
|
528
632
|
}
|
|
529
633
|
|
|
530
634
|
// Touch event methods (same as before)
|
|
531
|
-
recordTouchStart(
|
|
635
|
+
recordTouchStart(
|
|
636
|
+
x: number,
|
|
637
|
+
y: number,
|
|
638
|
+
target?: string,
|
|
639
|
+
pressure?: number
|
|
640
|
+
): void {
|
|
532
641
|
// Throttle touch events to prevent spam
|
|
533
|
-
const now = Date.now()
|
|
642
|
+
const now = Date.now();
|
|
534
643
|
if (now - this.lastTouchTime < this.touchThrottleMs) {
|
|
535
|
-
logger.debug(
|
|
536
|
-
|
|
644
|
+
logger.debug(
|
|
645
|
+
'GestureRecorder',
|
|
646
|
+
`Touch start throttled (${now - this.lastTouchTime}ms < ${this.touchThrottleMs}ms)`
|
|
647
|
+
);
|
|
648
|
+
return;
|
|
537
649
|
}
|
|
538
|
-
this.lastTouchTime = now
|
|
650
|
+
this.lastTouchTime = now;
|
|
539
651
|
|
|
540
|
-
logger.debug('GestureRecorder', 'Touch start recorded', {
|
|
541
|
-
|
|
652
|
+
logger.debug('GestureRecorder', 'Touch start recorded', {
|
|
653
|
+
x,
|
|
654
|
+
y,
|
|
655
|
+
target,
|
|
656
|
+
pressure,
|
|
657
|
+
});
|
|
658
|
+
this._createMouseInteractionEvent(x, y, MouseInteractions.TouchStart, now);
|
|
542
659
|
}
|
|
543
660
|
|
|
544
|
-
recordTouchMove(
|
|
661
|
+
recordTouchMove(
|
|
662
|
+
x: number,
|
|
663
|
+
y: number,
|
|
664
|
+
target?: string,
|
|
665
|
+
pressure?: number
|
|
666
|
+
): void {
|
|
545
667
|
// Throttle touch move events more aggressively
|
|
546
|
-
const now = Date.now()
|
|
547
|
-
if (now - this.lastTouchTime < this.touchThrottleMs * 2) {
|
|
548
|
-
|
|
549
|
-
|
|
668
|
+
const now = Date.now();
|
|
669
|
+
if (now - this.lastTouchTime < this.touchThrottleMs * 2) {
|
|
670
|
+
// 200ms throttle for move events
|
|
671
|
+
logger.debug(
|
|
672
|
+
'GestureRecorder',
|
|
673
|
+
`Touch move throttled (${now - this.lastTouchTime}ms < ${this.touchThrottleMs * 2}ms)`
|
|
674
|
+
);
|
|
675
|
+
return;
|
|
550
676
|
}
|
|
551
|
-
this.lastTouchTime = now
|
|
677
|
+
this.lastTouchTime = now;
|
|
552
678
|
|
|
553
|
-
logger.debug('GestureRecorder', 'Touch move recorded', {
|
|
554
|
-
|
|
679
|
+
logger.debug('GestureRecorder', 'Touch move recorded', {
|
|
680
|
+
x,
|
|
681
|
+
y,
|
|
682
|
+
target,
|
|
683
|
+
pressure,
|
|
684
|
+
});
|
|
685
|
+
this._createMouseMoveEvent(x, y, target);
|
|
555
686
|
}
|
|
556
687
|
|
|
557
|
-
recordTouchEnd(
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
688
|
+
recordTouchEnd(
|
|
689
|
+
x: number,
|
|
690
|
+
y: number,
|
|
691
|
+
target?: string,
|
|
692
|
+
pressure?: number
|
|
693
|
+
): void {
|
|
694
|
+
const timestamp = Date.now();
|
|
561
695
|
|
|
562
|
-
|
|
563
|
-
|
|
696
|
+
logger.debug('GestureRecorder', 'Touch end recorded', {
|
|
697
|
+
x,
|
|
698
|
+
y,
|
|
699
|
+
target,
|
|
700
|
+
pressure,
|
|
701
|
+
timestamp,
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
this.recordTap(x, y, target, pressure, timestamp);
|
|
705
|
+
this._createMouseInteractionEvent(
|
|
706
|
+
x,
|
|
707
|
+
y,
|
|
708
|
+
MouseInteractions.TouchEnd,
|
|
709
|
+
timestamp
|
|
710
|
+
);
|
|
564
711
|
|
|
565
712
|
// Only force screen capture on touch end (not on every touch event)
|
|
566
|
-
this.screenRecorder?.forceCapture(timestamp)
|
|
713
|
+
this.screenRecorder?.forceCapture(timestamp);
|
|
567
714
|
}
|
|
568
715
|
|
|
569
716
|
recordTouchCancel(x: number, y: number, _target?: string): void {
|
|
570
|
-
this._createMouseInteractionEvent(x, y, MouseInteractions.TouchCancel)
|
|
717
|
+
this._createMouseInteractionEvent(x, y, MouseInteractions.TouchCancel);
|
|
571
718
|
}
|
|
572
719
|
|
|
573
720
|
setImageNodeId(nodeId: number): void {
|
|
574
|
-
this.imageNodeId = nodeId
|
|
721
|
+
this.imageNodeId = nodeId;
|
|
575
722
|
}
|
|
576
723
|
|
|
577
724
|
private _recordEvent(event: GestureEvent): void {
|
|
578
|
-
if (!this.isRecording) return
|
|
725
|
+
if (!this.isRecording) return;
|
|
579
726
|
|
|
580
727
|
// Throttle gestures to avoid spam
|
|
581
|
-
const now = Date.now()
|
|
728
|
+
const now = Date.now();
|
|
582
729
|
if (now - this.lastGestureTime < this.gestureThrottleMs) {
|
|
583
|
-
return
|
|
730
|
+
return;
|
|
584
731
|
}
|
|
585
|
-
this.lastGestureTime = now
|
|
732
|
+
this.lastGestureTime = now;
|
|
586
733
|
|
|
587
|
-
this.events.push(event)
|
|
588
|
-
this._sendEvent(event)
|
|
589
|
-
this._recordOpenTelemetrySpan(event)
|
|
734
|
+
this.events.push(event);
|
|
735
|
+
this._sendEvent(event);
|
|
736
|
+
this._recordOpenTelemetrySpan(event);
|
|
590
737
|
}
|
|
591
738
|
|
|
592
739
|
private _createMouseInteractionEvent(
|
|
593
740
|
x: number,
|
|
594
741
|
y: number,
|
|
595
742
|
interactionType: MouseInteractions,
|
|
596
|
-
timestamp?: number
|
|
743
|
+
timestamp?: number
|
|
597
744
|
): void {
|
|
598
745
|
const incrementalSnapshotEvent: eventWithTime = {
|
|
599
746
|
type: EventType.IncrementalSnapshot,
|
|
@@ -606,9 +753,9 @@ export class GestureRecorder implements EventRecorder {
|
|
|
606
753
|
pointerType: 2, // 2 = Touch for React Native (0=Mouse, 1=Pen, 2=Touch)
|
|
607
754
|
},
|
|
608
755
|
timestamp: timestamp || Date.now(),
|
|
609
|
-
}
|
|
756
|
+
};
|
|
610
757
|
|
|
611
|
-
this.recordEvent(incrementalSnapshotEvent)
|
|
758
|
+
this.recordEvent(incrementalSnapshotEvent);
|
|
612
759
|
}
|
|
613
760
|
|
|
614
761
|
private _createMouseMoveEvent(x: number, y: number, _target?: string): void {
|
|
@@ -626,44 +773,44 @@ export class GestureRecorder implements EventRecorder {
|
|
|
626
773
|
],
|
|
627
774
|
},
|
|
628
775
|
timestamp: Date.now(),
|
|
629
|
-
}
|
|
776
|
+
};
|
|
630
777
|
|
|
631
|
-
this.recordEvent(incrementalSnapshotEvent)
|
|
778
|
+
this.recordEvent(incrementalSnapshotEvent);
|
|
632
779
|
}
|
|
633
780
|
|
|
634
781
|
recordEvent(event: any): void {
|
|
635
782
|
if (this.eventRecorder) {
|
|
636
|
-
this.eventRecorder.recordEvent(event)
|
|
783
|
+
this.eventRecorder.recordEvent(event);
|
|
637
784
|
}
|
|
638
785
|
}
|
|
639
786
|
|
|
640
787
|
// Get recorded events
|
|
641
788
|
getEvents(): GestureEvent[] {
|
|
642
|
-
return [...this.events]
|
|
789
|
+
return [...this.events];
|
|
643
790
|
}
|
|
644
791
|
|
|
645
792
|
// Clear events
|
|
646
793
|
clearEvents(): void {
|
|
647
|
-
this.events = []
|
|
794
|
+
this.events = [];
|
|
648
795
|
}
|
|
649
796
|
|
|
650
797
|
// Get event statistics
|
|
651
798
|
getEventStats(): Record<string, number> {
|
|
652
|
-
const stats: Record<string, number> = {}
|
|
653
|
-
this.events.forEach(event => {
|
|
654
|
-
stats[event.type] = (stats[event.type] || 0) + 1
|
|
655
|
-
})
|
|
656
|
-
return stats
|
|
799
|
+
const stats: Record<string, number> = {};
|
|
800
|
+
this.events.forEach((event) => {
|
|
801
|
+
stats[event.type] = (stats[event.type] || 0) + 1;
|
|
802
|
+
});
|
|
803
|
+
return stats;
|
|
657
804
|
}
|
|
658
805
|
|
|
659
806
|
// Set gesture throttle
|
|
660
807
|
setGestureThrottle(throttleMs: number): void {
|
|
661
|
-
this.gestureThrottleMs = throttleMs
|
|
808
|
+
this.gestureThrottleMs = throttleMs;
|
|
662
809
|
}
|
|
663
810
|
|
|
664
811
|
// Get recording status
|
|
665
812
|
isRecordingEnabled(): boolean {
|
|
666
|
-
return this.isRecording
|
|
813
|
+
return this.isRecording;
|
|
667
814
|
}
|
|
668
815
|
|
|
669
816
|
/**
|
|
@@ -674,8 +821,8 @@ export class GestureRecorder implements EventRecorder {
|
|
|
674
821
|
*/
|
|
675
822
|
private _truncateText(text: string, maxLength: number = 100): string {
|
|
676
823
|
if (!text || text.length <= maxLength) {
|
|
677
|
-
return text
|
|
824
|
+
return text;
|
|
678
825
|
}
|
|
679
|
-
return text.substring(0, maxLength - 3) + '...'
|
|
826
|
+
return text.substring(0, maxLength - 3) + '...';
|
|
680
827
|
}
|
|
681
828
|
}
|