@repliqo/sdk-react-native 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import { ScrollView, type ScrollViewProps } from 'react-native';
3
+ /**
4
+ * Drop-in replacement for React Native's ScrollView that automatically
5
+ * reports the scroll offset to Repliqo for accurate heatmaps.
6
+ *
7
+ * Without this, touch coordinates on scrollable screens are relative to
8
+ * the visible viewport. With this, they're relative to the full content,
9
+ * so heatmaps accurately show which content elements users touch.
10
+ *
11
+ * Usage — just replace `<ScrollView>` with `<RepliqoScrollView>`:
12
+ *
13
+ * ```tsx
14
+ * import { RepliqoScrollView } from '@repliqo/sdk-react-native';
15
+ *
16
+ * // Before:
17
+ * <ScrollView>...</ScrollView>
18
+ *
19
+ * // After:
20
+ * <RepliqoScrollView>...</RepliqoScrollView>
21
+ * ```
22
+ *
23
+ * All ScrollView props are forwarded. Your existing `onScroll` handler
24
+ * still fires normally.
25
+ */
26
+ export declare const RepliqoScrollView: React.ForwardRefExoticComponent<ScrollViewProps & React.RefAttributes<ScrollView>>;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.RepliqoScrollView = void 0;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_1 = require("react-native");
39
+ const client_1 = require("../core/client");
40
+ /**
41
+ * Drop-in replacement for React Native's ScrollView that automatically
42
+ * reports the scroll offset to Repliqo for accurate heatmaps.
43
+ *
44
+ * Without this, touch coordinates on scrollable screens are relative to
45
+ * the visible viewport. With this, they're relative to the full content,
46
+ * so heatmaps accurately show which content elements users touch.
47
+ *
48
+ * Usage — just replace `<ScrollView>` with `<RepliqoScrollView>`:
49
+ *
50
+ * ```tsx
51
+ * import { RepliqoScrollView } from '@repliqo/sdk-react-native';
52
+ *
53
+ * // Before:
54
+ * <ScrollView>...</ScrollView>
55
+ *
56
+ * // After:
57
+ * <RepliqoScrollView>...</RepliqoScrollView>
58
+ * ```
59
+ *
60
+ * All ScrollView props are forwarded. Your existing `onScroll` handler
61
+ * still fires normally.
62
+ */
63
+ exports.RepliqoScrollView = react_1.default.forwardRef(({ onScroll, ...props }, ref) => {
64
+ const handleScroll = (0, react_1.useCallback)((event) => {
65
+ // Report scroll offset to the SDK
66
+ try {
67
+ const offsetY = event.nativeEvent.contentOffset.y;
68
+ client_1.AppAnalytics.getInstance().setScrollOffset(offsetY);
69
+ }
70
+ catch {
71
+ // SDK not initialized, ignore
72
+ }
73
+ // Forward to the original onScroll handler
74
+ onScroll?.(event);
75
+ }, [onScroll]);
76
+ return (<react_native_1.ScrollView ref={ref} {...props} onScroll={handleScroll} scrollEventThrottle={props.scrollEventThrottle ?? 16}/>);
77
+ });
78
+ exports.RepliqoScrollView.displayName = 'RepliqoScrollView';
@@ -11,6 +11,7 @@ export declare class AppAnalytics {
11
11
  private sessionId;
12
12
  private currentScreen;
13
13
  private currentScreenEnteredAt;
14
+ private scrollOffsetY;
14
15
  private logger;
15
16
  private appStateSubscription;
16
17
  private constructor();
@@ -23,6 +24,13 @@ export declare class AppAnalytics {
23
24
  trackNavigation(fromScreen: string, toScreen: string): void;
24
25
  trackCustomEvent(eventName: string, data?: Record<string, any>): void;
25
26
  reportError(error: Error, metadata?: Record<string, any>): void;
27
+ /**
28
+ * Update the current scroll offset. Called by RepliqoScrollView
29
+ * or manually from the host app's ScrollView onScroll handler.
30
+ * The offset is added to touch Y coordinates for accurate heatmaps
31
+ * on scrollable screens.
32
+ */
33
+ setScrollOffset(y: number): void;
26
34
  onScreenEnter(screenName: string): void;
27
35
  onScreenExit(screenName: string): void;
28
36
  flush(): Promise<void>;
@@ -17,6 +17,7 @@ class AppAnalytics {
17
17
  this.sessionId = null;
18
18
  this.currentScreen = null;
19
19
  this.currentScreenEnteredAt = null;
20
+ this.scrollOffsetY = 0;
20
21
  this.appStateSubscription = null;
21
22
  this.config = (0, config_1.resolveConfig)(config);
22
23
  this.logger = new logger_1.Logger(this.config.debug);
@@ -163,10 +164,14 @@ class AppAnalytics {
163
164
  if (!this.config.enableTouchTracking) {
164
165
  return;
165
166
  }
167
+ // Adjust Y by the current scroll offset so touches map to content
168
+ // position, not just viewport position. This makes heatmaps accurate
169
+ // for scrollable screens.
170
+ const adjustedY = y + this.scrollOffsetY;
166
171
  const event = {
167
172
  type: 'touch',
168
173
  screenName: screenName || this.currentScreen || undefined,
169
- data: { x, y, ...extra },
174
+ data: { x, y: adjustedY, scrollOffsetY: this.scrollOffsetY, ...extra },
170
175
  timestamp: new Date().toISOString(),
171
176
  };
172
177
  this.eventQueue.add(event);
@@ -210,9 +215,19 @@ class AppAnalytics {
210
215
  }
211
216
  this.errorTracker.reportError(error, metadata);
212
217
  }
218
+ /**
219
+ * Update the current scroll offset. Called by RepliqoScrollView
220
+ * or manually from the host app's ScrollView onScroll handler.
221
+ * The offset is added to touch Y coordinates for accurate heatmaps
222
+ * on scrollable screens.
223
+ */
224
+ setScrollOffset(y) {
225
+ this.scrollOffsetY = y;
226
+ }
213
227
  onScreenEnter(screenName) {
214
228
  this.currentScreen = screenName;
215
229
  this.currentScreenEnteredAt = new Date().toISOString();
230
+ this.scrollOffsetY = 0; // Reset scroll on screen change
216
231
  this.errorTracker?.setCurrentScreen(screenName);
217
232
  this.snapshotCapture?.setCurrentScreen(screenName);
218
233
  this.logger.log('Screen entered:', screenName);
package/dist/index.d.ts CHANGED
@@ -11,5 +11,6 @@ export type { ScreenshotData, SnapshotPayload, } from './types/snapshot';
11
11
  export type { CrashReport } from './types/crash';
12
12
  export type { SnapshotCaptureConfig } from './snapshot/capture';
13
13
  export type { ScreenshotResult } from './snapshot/captureScreenshot';
14
+ export { RepliqoScrollView } from './components/RepliqoScrollView';
14
15
  export { DEFAULT_CONFIG } from './core/config';
15
16
  export type { ResolvedConfig } from './core/config';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_CONFIG = exports.captureNativeFrame = exports.isNativeScreenCaptureAvailable = exports.captureScreenshot = exports.SnapshotCapture = exports.ErrorTracker = exports.trackScreenExit = exports.trackScreenEnter = exports.trackScreenChange = exports.useNavigationTracker = exports.TouchTracker = exports.AppAnalytics = void 0;
3
+ exports.DEFAULT_CONFIG = exports.RepliqoScrollView = exports.captureNativeFrame = exports.isNativeScreenCaptureAvailable = exports.captureScreenshot = exports.SnapshotCapture = exports.ErrorTracker = exports.trackScreenExit = exports.trackScreenEnter = exports.trackScreenChange = exports.useNavigationTracker = exports.TouchTracker = exports.AppAnalytics = void 0;
4
4
  // Main class
5
5
  var client_1 = require("./core/client");
6
6
  Object.defineProperty(exports, "AppAnalytics", { enumerable: true, get: function () { return client_1.AppAnalytics; } });
@@ -23,6 +23,9 @@ Object.defineProperty(exports, "captureScreenshot", { enumerable: true, get: fun
23
23
  var NativeScreenCapture_1 = require("./snapshot/NativeScreenCapture");
24
24
  Object.defineProperty(exports, "isNativeScreenCaptureAvailable", { enumerable: true, get: function () { return NativeScreenCapture_1.isNativeScreenCaptureAvailable; } });
25
25
  Object.defineProperty(exports, "captureNativeFrame", { enumerable: true, get: function () { return NativeScreenCapture_1.captureNativeFrame; } });
26
+ // Components
27
+ var RepliqoScrollView_1 = require("./components/RepliqoScrollView");
28
+ Object.defineProperty(exports, "RepliqoScrollView", { enumerable: true, get: function () { return RepliqoScrollView_1.RepliqoScrollView; } });
26
29
  // Config
27
30
  var config_1 = require("./core/config");
28
31
  Object.defineProperty(exports, "DEFAULT_CONFIG", { enumerable: true, get: function () { return config_1.DEFAULT_CONFIG; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repliqo/sdk-react-native",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Session replay & analytics SDK for React Native. Captures screenshots (including modals/alerts), navigation, screen visits, and custom events.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,62 @@
1
+ import React, { useCallback } from 'react';
2
+ import {
3
+ ScrollView,
4
+ type ScrollViewProps,
5
+ type NativeSyntheticEvent,
6
+ type NativeScrollEvent,
7
+ } from 'react-native';
8
+ import { AppAnalytics } from '../core/client';
9
+
10
+ /**
11
+ * Drop-in replacement for React Native's ScrollView that automatically
12
+ * reports the scroll offset to Repliqo for accurate heatmaps.
13
+ *
14
+ * Without this, touch coordinates on scrollable screens are relative to
15
+ * the visible viewport. With this, they're relative to the full content,
16
+ * so heatmaps accurately show which content elements users touch.
17
+ *
18
+ * Usage — just replace `<ScrollView>` with `<RepliqoScrollView>`:
19
+ *
20
+ * ```tsx
21
+ * import { RepliqoScrollView } from '@repliqo/sdk-react-native';
22
+ *
23
+ * // Before:
24
+ * <ScrollView>...</ScrollView>
25
+ *
26
+ * // After:
27
+ * <RepliqoScrollView>...</RepliqoScrollView>
28
+ * ```
29
+ *
30
+ * All ScrollView props are forwarded. Your existing `onScroll` handler
31
+ * still fires normally.
32
+ */
33
+ export const RepliqoScrollView = React.forwardRef<ScrollView, ScrollViewProps>(
34
+ ({ onScroll, ...props }, ref) => {
35
+ const handleScroll = useCallback(
36
+ (event: NativeSyntheticEvent<NativeScrollEvent>) => {
37
+ // Report scroll offset to the SDK
38
+ try {
39
+ const offsetY = event.nativeEvent.contentOffset.y;
40
+ AppAnalytics.getInstance().setScrollOffset(offsetY);
41
+ } catch {
42
+ // SDK not initialized, ignore
43
+ }
44
+
45
+ // Forward to the original onScroll handler
46
+ onScroll?.(event);
47
+ },
48
+ [onScroll],
49
+ );
50
+
51
+ return (
52
+ <ScrollView
53
+ ref={ref}
54
+ {...props}
55
+ onScroll={handleScroll}
56
+ scrollEventThrottle={props.scrollEventThrottle ?? 16}
57
+ />
58
+ );
59
+ },
60
+ );
61
+
62
+ RepliqoScrollView.displayName = 'RepliqoScrollView';
@@ -28,6 +28,7 @@ export class AppAnalytics {
28
28
  private sessionId: string | null = null;
29
29
  private currentScreen: string | null = null;
30
30
  private currentScreenEnteredAt: string | null = null;
31
+ private scrollOffsetY: number = 0;
31
32
  private logger: Logger;
32
33
  private appStateSubscription: { remove: () => void } | null = null;
33
34
 
@@ -246,10 +247,15 @@ export class AppAnalytics {
246
247
  return;
247
248
  }
248
249
 
250
+ // Adjust Y by the current scroll offset so touches map to content
251
+ // position, not just viewport position. This makes heatmaps accurate
252
+ // for scrollable screens.
253
+ const adjustedY = y + this.scrollOffsetY;
254
+
249
255
  const event: AnalyticsEvent = {
250
256
  type: 'touch',
251
257
  screenName: screenName || this.currentScreen || undefined,
252
- data: { x, y, ...extra },
258
+ data: { x, y: adjustedY, scrollOffsetY: this.scrollOffsetY, ...extra },
253
259
  timestamp: new Date().toISOString(),
254
260
  };
255
261
 
@@ -306,9 +312,20 @@ export class AppAnalytics {
306
312
  this.errorTracker.reportError(error, metadata);
307
313
  }
308
314
 
315
+ /**
316
+ * Update the current scroll offset. Called by RepliqoScrollView
317
+ * or manually from the host app's ScrollView onScroll handler.
318
+ * The offset is added to touch Y coordinates for accurate heatmaps
319
+ * on scrollable screens.
320
+ */
321
+ setScrollOffset(y: number): void {
322
+ this.scrollOffsetY = y;
323
+ }
324
+
309
325
  onScreenEnter(screenName: string): void {
310
326
  this.currentScreen = screenName;
311
327
  this.currentScreenEnteredAt = new Date().toISOString();
328
+ this.scrollOffsetY = 0; // Reset scroll on screen change
312
329
  this.errorTracker?.setCurrentScreen(screenName);
313
330
  this.snapshotCapture?.setCurrentScreen(screenName);
314
331
  this.logger.log('Screen entered:', screenName);
package/src/index.ts CHANGED
@@ -42,6 +42,9 @@ export type { CrashReport } from './types/crash';
42
42
  export type { SnapshotCaptureConfig } from './snapshot/capture';
43
43
  export type { ScreenshotResult } from './snapshot/captureScreenshot';
44
44
 
45
+ // Components
46
+ export { RepliqoScrollView } from './components/RepliqoScrollView';
47
+
45
48
  // Config
46
49
  export { DEFAULT_CONFIG } from './core/config';
47
50
  export type { ResolvedConfig } from './core/config';