@repliqo/sdk-react-native 0.3.6 → 0.3.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/dist/components/RepliqoScrollView.js +7 -1
- package/dist/core/client.d.ts +9 -4
- package/dist/core/client.js +11 -5
- package/dist/transport/api.client.d.ts +8 -1
- package/dist/transport/api.client.js +25 -11
- package/package.json +1 -1
- package/src/components/RepliqoScrollView.tsx +9 -0
- package/src/core/client.ts +13 -3
- package/src/transport/api.client.ts +27 -10
|
@@ -79,6 +79,10 @@ exports.RepliqoScrollView = react_1.default.forwardRef(({ onScroll, onMomentumSc
|
|
|
79
79
|
const scrollViewId = (0, react_1.useRef)(nextScrollViewId());
|
|
80
80
|
const innerRef = (0, react_1.useRef)(null);
|
|
81
81
|
const isMounted = (0, react_1.useRef)(true);
|
|
82
|
+
// Last measured window-relative top Y of the ScrollView — passed to
|
|
83
|
+
// captureScrollTile so the backend can crop each tile to just the
|
|
84
|
+
// scroll viewport area (no fixed header/footer duplication).
|
|
85
|
+
const lastViewportTop = (0, react_1.useRef)(null);
|
|
82
86
|
const scrollState = (0, react_1.useRef)(null);
|
|
83
87
|
// Forward the inner ref to the parent's ref (if provided).
|
|
84
88
|
// IMPORTANT: do NOT clear innerRef on cleanup — RN sometimes calls the
|
|
@@ -112,6 +116,7 @@ exports.RepliqoScrollView = react_1.default.forwardRef(({ onScroll, onMomentumSc
|
|
|
112
116
|
// ignore those, the next onLayout/onScroll will re-measure.
|
|
113
117
|
if (width <= 0 || height <= 0)
|
|
114
118
|
return;
|
|
119
|
+
lastViewportTop.current = y;
|
|
115
120
|
try {
|
|
116
121
|
client_1.AppAnalytics.getInstance().registerScrollView(scrollViewId.current, {
|
|
117
122
|
x, y, width, height,
|
|
@@ -166,7 +171,8 @@ exports.RepliqoScrollView = react_1.default.forwardRef(({ onScroll, onMomentumSc
|
|
|
166
171
|
lastCaptureY.current = state.offsetY;
|
|
167
172
|
initialCaptured.current = true;
|
|
168
173
|
try {
|
|
169
|
-
|
|
174
|
+
const windowHeight = react_native_1.Dimensions.get('window').height;
|
|
175
|
+
client_1.AppAnalytics.getInstance().captureScrollTile(state.offsetY, state.viewportHeight, state.contentHeight, lastViewportTop.current ?? undefined, windowHeight);
|
|
170
176
|
}
|
|
171
177
|
catch { }
|
|
172
178
|
};
|
package/dist/core/client.d.ts
CHANGED
|
@@ -85,11 +85,16 @@ export declare class AppAnalytics {
|
|
|
85
85
|
* Capture the current viewport as a tile for collaborative screen
|
|
86
86
|
* building. Called by RepliqoScrollView on scroll stops.
|
|
87
87
|
*
|
|
88
|
-
* The tile includes
|
|
89
|
-
* in the full content this viewport belongs
|
|
90
|
-
*
|
|
88
|
+
* The tile includes:
|
|
89
|
+
* - `scrollOffsetY`: where in the full content this viewport belongs
|
|
90
|
+
* - `viewportTop`: Y position (window logical px) of the ScrollView's
|
|
91
|
+
* top edge. Used by the backend to crop each tile to just the
|
|
92
|
+
* scrollable area, removing fixed headers/footers that would
|
|
93
|
+
* otherwise duplicate in the composite.
|
|
94
|
+
* - `windowHeight`: window height in logical px, for image-to-logical
|
|
95
|
+
* scale computation during backend cropping.
|
|
91
96
|
*/
|
|
92
|
-
captureScrollTile(scrollOffsetY: number, viewportHeight: number, contentHeight: number): void;
|
|
97
|
+
captureScrollTile(scrollOffsetY: number, viewportHeight: number, contentHeight: number, viewportTop?: number, windowHeight?: number): void;
|
|
93
98
|
/**
|
|
94
99
|
* Programmatically scroll-scan the current ScrollView and upload all
|
|
95
100
|
* tiles to the backend. Invisible to the user (~300-500ms).
|
package/dist/core/client.js
CHANGED
|
@@ -331,11 +331,16 @@ class AppAnalytics {
|
|
|
331
331
|
* Capture the current viewport as a tile for collaborative screen
|
|
332
332
|
* building. Called by RepliqoScrollView on scroll stops.
|
|
333
333
|
*
|
|
334
|
-
* The tile includes
|
|
335
|
-
* in the full content this viewport belongs
|
|
336
|
-
*
|
|
334
|
+
* The tile includes:
|
|
335
|
+
* - `scrollOffsetY`: where in the full content this viewport belongs
|
|
336
|
+
* - `viewportTop`: Y position (window logical px) of the ScrollView's
|
|
337
|
+
* top edge. Used by the backend to crop each tile to just the
|
|
338
|
+
* scrollable area, removing fixed headers/footers that would
|
|
339
|
+
* otherwise duplicate in the composite.
|
|
340
|
+
* - `windowHeight`: window height in logical px, for image-to-logical
|
|
341
|
+
* scale computation during backend cropping.
|
|
337
342
|
*/
|
|
338
|
-
captureScrollTile(scrollOffsetY, viewportHeight, contentHeight) {
|
|
343
|
+
captureScrollTile(scrollOffsetY, viewportHeight, contentHeight, viewportTop, windowHeight) {
|
|
339
344
|
if (!this.sessionId || !this.currentScreen)
|
|
340
345
|
return;
|
|
341
346
|
if (viewportHeight <= 0)
|
|
@@ -350,8 +355,9 @@ class AppAnalytics {
|
|
|
350
355
|
return;
|
|
351
356
|
this.logger.log(`Scroll tile: "${screenName}" Y=${Math.round(scrollOffsetY)} ` +
|
|
352
357
|
`vp=${Math.round(viewportHeight)} content=${Math.round(contentHeight)} ` +
|
|
358
|
+
`top=${viewportTop !== undefined ? Math.round(viewportTop) : '?'} ` +
|
|
353
359
|
`${Math.round(frame.image.length / 1024)}KB`);
|
|
354
|
-
await this.apiClient.uploadScreenTile(sessionId, screenName, frame.image, frame.width, frame.height, scrollOffsetY, viewportHeight, contentHeight);
|
|
360
|
+
await this.apiClient.uploadScreenTile(sessionId, screenName, frame.image, frame.width, frame.height, scrollOffsetY, viewportHeight, contentHeight, viewportTop, windowHeight);
|
|
355
361
|
}
|
|
356
362
|
catch (err) {
|
|
357
363
|
this.logger.warn('Scroll tile failed:', err);
|
|
@@ -32,7 +32,14 @@ export declare class ApiClient {
|
|
|
32
32
|
*/
|
|
33
33
|
/**
|
|
34
34
|
* Upload a scroll tile for collaborative screen building.
|
|
35
|
+
*
|
|
36
|
+
* @param viewportTop Y position (window logical px) of the ScrollView's
|
|
37
|
+
* top edge. Used by the backend to crop each tile to just the
|
|
38
|
+
* scrollable area, removing fixed headers/footers that would
|
|
39
|
+
* otherwise duplicate in the composite.
|
|
40
|
+
* @param windowHeight Window height (logical px) at capture time.
|
|
41
|
+
* Used by the backend to compute image-to-logical scale for cropping.
|
|
35
42
|
*/
|
|
36
|
-
uploadScreenTile(sessionId: string, screenName: string, image: string, width: number, height: number, scrollOffsetY: number, viewportHeight: number, contentHeight: number): Promise<void>;
|
|
43
|
+
uploadScreenTile(sessionId: string, screenName: string, image: string, width: number, height: number, scrollOffsetY: number, viewportHeight: number, contentHeight: number, viewportTop?: number, windowHeight?: number): Promise<void>;
|
|
37
44
|
uploadScreenCapture(screenName: string, image: string, width: number, height: number, isFullContent: boolean): Promise<void>;
|
|
38
45
|
}
|
|
@@ -158,22 +158,36 @@ class ApiClient {
|
|
|
158
158
|
*/
|
|
159
159
|
/**
|
|
160
160
|
* Upload a scroll tile for collaborative screen building.
|
|
161
|
+
*
|
|
162
|
+
* @param viewportTop Y position (window logical px) of the ScrollView's
|
|
163
|
+
* top edge. Used by the backend to crop each tile to just the
|
|
164
|
+
* scrollable area, removing fixed headers/footers that would
|
|
165
|
+
* otherwise duplicate in the composite.
|
|
166
|
+
* @param windowHeight Window height (logical px) at capture time.
|
|
167
|
+
* Used by the backend to compute image-to-logical scale for cropping.
|
|
161
168
|
*/
|
|
162
|
-
async uploadScreenTile(sessionId, screenName, image, width, height, scrollOffsetY, viewportHeight, contentHeight) {
|
|
169
|
+
async uploadScreenTile(sessionId, screenName, image, width, height, scrollOffsetY, viewportHeight, contentHeight, viewportTop, windowHeight) {
|
|
163
170
|
try {
|
|
171
|
+
const body = {
|
|
172
|
+
sessionId,
|
|
173
|
+
screenName,
|
|
174
|
+
image,
|
|
175
|
+
width,
|
|
176
|
+
height,
|
|
177
|
+
scrollOffsetY: Math.round(scrollOffsetY),
|
|
178
|
+
viewportHeight: Math.round(viewportHeight),
|
|
179
|
+
contentHeight: Math.round(contentHeight),
|
|
180
|
+
};
|
|
181
|
+
if (typeof viewportTop === 'number' && Number.isFinite(viewportTop)) {
|
|
182
|
+
body.viewportTop = Math.round(viewportTop);
|
|
183
|
+
}
|
|
184
|
+
if (typeof windowHeight === 'number' && Number.isFinite(windowHeight)) {
|
|
185
|
+
body.windowHeight = Math.round(windowHeight);
|
|
186
|
+
}
|
|
164
187
|
const response = await fetch(`${this.baseUrl}/api/screen-tiles`, {
|
|
165
188
|
method: 'POST',
|
|
166
189
|
headers: this.getHeaders(),
|
|
167
|
-
body: JSON.stringify(
|
|
168
|
-
sessionId,
|
|
169
|
-
screenName,
|
|
170
|
-
image,
|
|
171
|
-
width,
|
|
172
|
-
height,
|
|
173
|
-
scrollOffsetY: Math.round(scrollOffsetY),
|
|
174
|
-
viewportHeight: Math.round(viewportHeight),
|
|
175
|
-
contentHeight: Math.round(contentHeight),
|
|
176
|
-
}),
|
|
190
|
+
body: JSON.stringify(body),
|
|
177
191
|
});
|
|
178
192
|
if (!response.ok) {
|
|
179
193
|
const text = await response.text();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@repliqo/sdk-react-native",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
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",
|
|
@@ -2,6 +2,7 @@ import React, { useCallback, useRef, useEffect } from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
ScrollView,
|
|
4
4
|
Keyboard,
|
|
5
|
+
Dimensions,
|
|
5
6
|
type ScrollViewProps,
|
|
6
7
|
type NativeSyntheticEvent,
|
|
7
8
|
type NativeScrollEvent,
|
|
@@ -54,6 +55,10 @@ export const RepliqoScrollView = React.forwardRef<ScrollView, ScrollViewProps>(
|
|
|
54
55
|
const scrollViewId = useRef<string>(nextScrollViewId());
|
|
55
56
|
const innerRef = useRef<ScrollView | null>(null);
|
|
56
57
|
const isMounted = useRef(true);
|
|
58
|
+
// Last measured window-relative top Y of the ScrollView — passed to
|
|
59
|
+
// captureScrollTile so the backend can crop each tile to just the
|
|
60
|
+
// scroll viewport area (no fixed header/footer duplication).
|
|
61
|
+
const lastViewportTop = useRef<number | null>(null);
|
|
57
62
|
const scrollState = useRef<{
|
|
58
63
|
offsetY: number;
|
|
59
64
|
viewportHeight: number;
|
|
@@ -88,6 +93,7 @@ export const RepliqoScrollView = React.forwardRef<ScrollView, ScrollViewProps>(
|
|
|
88
93
|
// RN sometimes returns 0/0 dims before the view is positioned —
|
|
89
94
|
// ignore those, the next onLayout/onScroll will re-measure.
|
|
90
95
|
if (width <= 0 || height <= 0) return;
|
|
96
|
+
lastViewportTop.current = y;
|
|
91
97
|
try {
|
|
92
98
|
AppAnalytics.getInstance().registerScrollView(scrollViewId.current, {
|
|
93
99
|
x, y, width, height,
|
|
@@ -141,10 +147,13 @@ export const RepliqoScrollView = React.forwardRef<ScrollView, ScrollViewProps>(
|
|
|
141
147
|
initialCaptured.current = true;
|
|
142
148
|
|
|
143
149
|
try {
|
|
150
|
+
const windowHeight = Dimensions.get('window').height;
|
|
144
151
|
AppAnalytics.getInstance().captureScrollTile(
|
|
145
152
|
state.offsetY,
|
|
146
153
|
state.viewportHeight,
|
|
147
154
|
state.contentHeight,
|
|
155
|
+
lastViewportTop.current ?? undefined,
|
|
156
|
+
windowHeight,
|
|
148
157
|
);
|
|
149
158
|
} catch {}
|
|
150
159
|
};
|
package/src/core/client.ts
CHANGED
|
@@ -446,14 +446,21 @@ export class AppAnalytics {
|
|
|
446
446
|
* Capture the current viewport as a tile for collaborative screen
|
|
447
447
|
* building. Called by RepliqoScrollView on scroll stops.
|
|
448
448
|
*
|
|
449
|
-
* The tile includes
|
|
450
|
-
* in the full content this viewport belongs
|
|
451
|
-
*
|
|
449
|
+
* The tile includes:
|
|
450
|
+
* - `scrollOffsetY`: where in the full content this viewport belongs
|
|
451
|
+
* - `viewportTop`: Y position (window logical px) of the ScrollView's
|
|
452
|
+
* top edge. Used by the backend to crop each tile to just the
|
|
453
|
+
* scrollable area, removing fixed headers/footers that would
|
|
454
|
+
* otherwise duplicate in the composite.
|
|
455
|
+
* - `windowHeight`: window height in logical px, for image-to-logical
|
|
456
|
+
* scale computation during backend cropping.
|
|
452
457
|
*/
|
|
453
458
|
captureScrollTile(
|
|
454
459
|
scrollOffsetY: number,
|
|
455
460
|
viewportHeight: number,
|
|
456
461
|
contentHeight: number,
|
|
462
|
+
viewportTop?: number,
|
|
463
|
+
windowHeight?: number,
|
|
457
464
|
): void {
|
|
458
465
|
if (!this.sessionId || !this.currentScreen) return;
|
|
459
466
|
if (viewportHeight <= 0) return; // No real dimensions yet
|
|
@@ -470,6 +477,7 @@ export class AppAnalytics {
|
|
|
470
477
|
this.logger.log(
|
|
471
478
|
`Scroll tile: "${screenName}" Y=${Math.round(scrollOffsetY)} ` +
|
|
472
479
|
`vp=${Math.round(viewportHeight)} content=${Math.round(contentHeight)} ` +
|
|
480
|
+
`top=${viewportTop !== undefined ? Math.round(viewportTop) : '?'} ` +
|
|
473
481
|
`${Math.round(frame.image.length / 1024)}KB`,
|
|
474
482
|
);
|
|
475
483
|
|
|
@@ -482,6 +490,8 @@ export class AppAnalytics {
|
|
|
482
490
|
scrollOffsetY,
|
|
483
491
|
viewportHeight,
|
|
484
492
|
contentHeight,
|
|
493
|
+
viewportTop,
|
|
494
|
+
windowHeight,
|
|
485
495
|
);
|
|
486
496
|
} catch (err) {
|
|
487
497
|
this.logger.warn('Scroll tile failed:', err);
|
|
@@ -229,6 +229,13 @@ export class ApiClient {
|
|
|
229
229
|
*/
|
|
230
230
|
/**
|
|
231
231
|
* Upload a scroll tile for collaborative screen building.
|
|
232
|
+
*
|
|
233
|
+
* @param viewportTop Y position (window logical px) of the ScrollView's
|
|
234
|
+
* top edge. Used by the backend to crop each tile to just the
|
|
235
|
+
* scrollable area, removing fixed headers/footers that would
|
|
236
|
+
* otherwise duplicate in the composite.
|
|
237
|
+
* @param windowHeight Window height (logical px) at capture time.
|
|
238
|
+
* Used by the backend to compute image-to-logical scale for cropping.
|
|
232
239
|
*/
|
|
233
240
|
async uploadScreenTile(
|
|
234
241
|
sessionId: string,
|
|
@@ -239,23 +246,33 @@ export class ApiClient {
|
|
|
239
246
|
scrollOffsetY: number,
|
|
240
247
|
viewportHeight: number,
|
|
241
248
|
contentHeight: number,
|
|
249
|
+
viewportTop?: number,
|
|
250
|
+
windowHeight?: number,
|
|
242
251
|
): Promise<void> {
|
|
243
252
|
try {
|
|
253
|
+
const body: Record<string, unknown> = {
|
|
254
|
+
sessionId,
|
|
255
|
+
screenName,
|
|
256
|
+
image,
|
|
257
|
+
width,
|
|
258
|
+
height,
|
|
259
|
+
scrollOffsetY: Math.round(scrollOffsetY),
|
|
260
|
+
viewportHeight: Math.round(viewportHeight),
|
|
261
|
+
contentHeight: Math.round(contentHeight),
|
|
262
|
+
};
|
|
263
|
+
if (typeof viewportTop === 'number' && Number.isFinite(viewportTop)) {
|
|
264
|
+
body.viewportTop = Math.round(viewportTop);
|
|
265
|
+
}
|
|
266
|
+
if (typeof windowHeight === 'number' && Number.isFinite(windowHeight)) {
|
|
267
|
+
body.windowHeight = Math.round(windowHeight);
|
|
268
|
+
}
|
|
269
|
+
|
|
244
270
|
const response = await fetch(
|
|
245
271
|
`${this.baseUrl}/api/screen-tiles`,
|
|
246
272
|
{
|
|
247
273
|
method: 'POST',
|
|
248
274
|
headers: this.getHeaders(),
|
|
249
|
-
body: JSON.stringify(
|
|
250
|
-
sessionId,
|
|
251
|
-
screenName,
|
|
252
|
-
image,
|
|
253
|
-
width,
|
|
254
|
-
height,
|
|
255
|
-
scrollOffsetY: Math.round(scrollOffsetY),
|
|
256
|
-
viewportHeight: Math.round(viewportHeight),
|
|
257
|
-
contentHeight: Math.round(contentHeight),
|
|
258
|
-
}),
|
|
275
|
+
body: JSON.stringify(body),
|
|
259
276
|
},
|
|
260
277
|
);
|
|
261
278
|
|