@repliqo/sdk-react-native 0.1.0
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/INTEGRATION_GUIDE.md +1312 -0
- package/android/build.gradle +24 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/repliqo/screencapture/MultiWindowCapture.java +230 -0
- package/android/src/main/java/com/repliqo/screencapture/ScreenCaptureModule.java +94 -0
- package/android/src/main/java/com/repliqo/screencapture/ScreenCapturePackage.java +38 -0
- package/dist/components/ScreenCapturePermission.d.ts +55 -0
- package/dist/components/ScreenCapturePermission.js +112 -0
- package/dist/core/client.d.ts +41 -0
- package/dist/core/client.js +309 -0
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +26 -0
- package/dist/core/logger.d.ts +8 -0
- package/dist/core/logger.js +25 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +28 -0
- package/dist/snapshot/NativeScreenCapture.d.ts +17 -0
- package/dist/snapshot/NativeScreenCapture.js +38 -0
- package/dist/snapshot/capture.d.ts +36 -0
- package/dist/snapshot/capture.js +109 -0
- package/dist/snapshot/captureScreenshot.d.ts +26 -0
- package/dist/snapshot/captureScreenshot.js +89 -0
- package/dist/trackers/error.tracker.d.ts +22 -0
- package/dist/trackers/error.tracker.js +123 -0
- package/dist/trackers/navigation.tracker.d.ts +6 -0
- package/dist/trackers/navigation.tracker.js +78 -0
- package/dist/trackers/screen.tracker.d.ts +2 -0
- package/dist/trackers/screen.tracker.js +23 -0
- package/dist/trackers/touch.tracker.d.ts +8 -0
- package/dist/trackers/touch.tracker.js +30 -0
- package/dist/transport/api.client.d.ts +29 -0
- package/dist/transport/api.client.js +156 -0
- package/dist/transport/batch-queue.d.ts +18 -0
- package/dist/transport/batch-queue.js +80 -0
- package/dist/types/crash.d.ts +10 -0
- package/dist/types/crash.js +2 -0
- package/dist/types/events.d.ts +53 -0
- package/dist/types/events.js +2 -0
- package/dist/types/snapshot.d.ts +21 -0
- package/dist/types/snapshot.js +9 -0
- package/package.json +64 -0
- package/src/components/ScreenCapturePermission.tsx +160 -0
- package/src/core/client.ts +425 -0
- package/src/core/config.ts +27 -0
- package/src/core/logger.ts +27 -0
- package/src/index.ts +47 -0
- package/src/snapshot/NativeScreenCapture.ts +54 -0
- package/src/snapshot/capture.ts +142 -0
- package/src/snapshot/captureScreenshot.ts +109 -0
- package/src/trackers/error.tracker.ts +136 -0
- package/src/trackers/navigation.tracker.ts +98 -0
- package/src/trackers/screen.tracker.ts +19 -0
- package/src/trackers/touch.tracker.tsx +44 -0
- package/src/transport/api.client.ts +225 -0
- package/src/transport/batch-queue.ts +95 -0
- package/src/types/crash.ts +10 -0
- package/src/types/events.ts +59 -0
- package/src/types/snapshot.ts +23 -0
|
@@ -0,0 +1,1312 @@
|
|
|
1
|
+
# Repliqo SDK for React Native - Integration Guide
|
|
2
|
+
|
|
3
|
+
> **Version:** 0.1.0
|
|
4
|
+
> **Package:** `@repliqo/sdk-react-native`
|
|
5
|
+
> **Last updated:** March 2026
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
1. [Overview](#1-overview)
|
|
12
|
+
2. [Prerequisites](#2-prerequisites)
|
|
13
|
+
3. [Installation](#3-installation)
|
|
14
|
+
4. [Quick Start (5-Minute Setup)](#4-quick-start-5-minute-setup)
|
|
15
|
+
5. [Configuration Reference](#5-configuration-reference)
|
|
16
|
+
6. [Navigation Tracking (Automatic)](#6-navigation-tracking-automatic)
|
|
17
|
+
7. [Custom Events](#7-custom-events)
|
|
18
|
+
8. [Error/Crash Tracking](#8-errorcrash-tracking)
|
|
19
|
+
9. [Manual Screen Tracking](#9-manual-screen-tracking)
|
|
20
|
+
10. [Session Management](#10-session-management)
|
|
21
|
+
11. [Device Info](#11-device-info)
|
|
22
|
+
12. [Advanced: Flush Control](#12-advanced-flush-control)
|
|
23
|
+
13. [Advanced: Debug Mode](#13-advanced-debug-mode)
|
|
24
|
+
14. [Dashboard Guide](#14-dashboard-guide)
|
|
25
|
+
15. [API Reference](#15-api-reference)
|
|
26
|
+
16. [Screen Capture Architecture](#16-screen-capture-architecture)
|
|
27
|
+
17. [Troubleshooting](#17-troubleshooting)
|
|
28
|
+
17. [TypeScript Types](#17-typescript-types)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. Overview
|
|
33
|
+
|
|
34
|
+
Repliqo is a lightweight analytics SDK designed specifically for React Native applications. It provides developers with deep visibility into how users interact with their mobile apps by capturing navigation patterns, screen visits, touch events, crashes, and custom events -- all with minimal performance overhead and zero impact on your app's startup time.
|
|
35
|
+
|
|
36
|
+
The SDK works on a buffer-and-batch architecture: events are collected in memory as users interact with your app, then sent to the Repliqo backend in efficient batches. The backend stores and processes this data, which you can then explore through the Repliqo dashboard. The dashboard provides session replays, heatmaps, funnel analysis, crash reports, and data export capabilities. Because events are batched rather than sent one-by-one, the SDK keeps network usage low and never blocks the main thread.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 2. Prerequisites
|
|
41
|
+
|
|
42
|
+
Before integrating the Repliqo SDK, make sure your project meets the following requirements:
|
|
43
|
+
|
|
44
|
+
- **React Native** >= 0.68.0
|
|
45
|
+
- **React** >= 17.0.0
|
|
46
|
+
- **@react-navigation/native** >= 6.0.0 (required only if you want automatic navigation tracking)
|
|
47
|
+
- **A Repliqo account with an API key**
|
|
48
|
+
|
|
49
|
+
### Getting Your API Key
|
|
50
|
+
|
|
51
|
+
1. Go to the Repliqo dashboard at **[https://repliqo.odmservice.site](https://repliqo.odmservice.site)**.
|
|
52
|
+
2. Sign in or create a new account.
|
|
53
|
+
3. Navigate to **Settings > Applications** and click **Create Application**.
|
|
54
|
+
4. Give your app a name (this becomes your `appId`) and note the **API key** that is generated.
|
|
55
|
+
5. Copy both the `appId` and the `apiKey` -- you will need them during SDK initialization.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 3. Installation
|
|
60
|
+
|
|
61
|
+
### From a Local Path (Development)
|
|
62
|
+
|
|
63
|
+
If you have the SDK source code locally (e.g., in a monorepo or cloned repository), install it by referencing the local path:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install file:../path/to/sdk-react-native
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For example, if your project structure looks like this:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
my-workspace/
|
|
73
|
+
app-analytics-api/
|
|
74
|
+
packages/
|
|
75
|
+
sdk-react-native/ # <-- The SDK
|
|
76
|
+
my-react-native-app/ # <-- Your app
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Run this from your React Native app directory:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install file:../app-analytics-api/packages/sdk-react-native
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### From npm (Future)
|
|
86
|
+
|
|
87
|
+
Once the SDK is published to npm, you will be able to install it with:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm install @repliqo/sdk-react-native
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Post-Installation
|
|
94
|
+
|
|
95
|
+
After installing, if Metro bundler has trouble resolving the module, clear the cache:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx react-native start --reset-cache
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The SDK includes a native Android module for **multi-window screen capture** (captures modals, alerts, and dialogs). It autolinks automatically via React Native CLI — no manual linking, no `settings.gradle` edits, no `MainApplication` changes needed.
|
|
102
|
+
|
|
103
|
+
On Android, the native module uses `View.draw()` on all app windows to composite a full screenshot including overlays. **No permissions are required** — it does not use MediaProjection or screen recording APIs.
|
|
104
|
+
|
|
105
|
+
On iOS, the native module is not yet implemented. The SDK falls back to `react-native-view-shot` which captures the main view only.
|
|
106
|
+
|
|
107
|
+
> **Note:** After installing, you must rebuild the native app (`npx react-native run-android`) for the native module to be linked. A Metro-only restart is not sufficient.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 4. Quick Start (5-Minute Setup)
|
|
112
|
+
|
|
113
|
+
This section gets you up and running with the absolute minimum configuration. After completing these two steps, navigation tracking and screen visit analytics will work automatically.
|
|
114
|
+
|
|
115
|
+
### Step 1: Create the Analytics Service File
|
|
116
|
+
|
|
117
|
+
Create a file at `src/services/repliqo.ts` in your project:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// src/services/repliqo.ts
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Repliqo Analytics SDK - Initialization and session management.
|
|
124
|
+
*
|
|
125
|
+
* This file initializes the SDK and starts a session with device info.
|
|
126
|
+
* Import and call initRepliqo() from your App.tsx on startup.
|
|
127
|
+
*/
|
|
128
|
+
import { AppAnalytics } from '@repliqo/sdk-react-native';
|
|
129
|
+
import { Platform, Dimensions } from 'react-native';
|
|
130
|
+
|
|
131
|
+
// Replace these with your actual values from the Repliqo dashboard
|
|
132
|
+
const REPLIQO_API_KEY = 'your-api-key-here';
|
|
133
|
+
const REPLIQO_APP_ID = 'your-app-id';
|
|
134
|
+
|
|
135
|
+
// Use localhost for development, production URL for release builds
|
|
136
|
+
const REPLIQO_API_URL = __DEV__
|
|
137
|
+
? 'http://10.0.2.2:3050' // Android emulator localhost
|
|
138
|
+
: 'https://api-repliqo.odmservice.site';
|
|
139
|
+
|
|
140
|
+
// Guard against double-initialization
|
|
141
|
+
let initialized = false;
|
|
142
|
+
|
|
143
|
+
export async function initRepliqo(): Promise<void> {
|
|
144
|
+
if (initialized) return;
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
// 1. Initialize the SDK with your configuration
|
|
148
|
+
AppAnalytics.init({
|
|
149
|
+
apiKey: REPLIQO_API_KEY,
|
|
150
|
+
appId: REPLIQO_APP_ID,
|
|
151
|
+
baseUrl: REPLIQO_API_URL,
|
|
152
|
+
enableTouchTracking: false, // Disable if you don't need heatmaps
|
|
153
|
+
enableNavigationTracking: true, // Automatic navigation events
|
|
154
|
+
enableCrashTracking: true, // Capture JS errors and unhandled rejections
|
|
155
|
+
enableSnapshots: false, // Disable for lower overhead
|
|
156
|
+
batchSize: 20, // Send events in groups of 20
|
|
157
|
+
flushInterval: 30000, // Flush every 30 seconds
|
|
158
|
+
debug: __DEV__, // Log SDK activity in development
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// 2. Gather device information
|
|
162
|
+
const { width, height } = Dimensions.get('window');
|
|
163
|
+
const deviceId = `${Platform.OS}-${Math.random().toString(36).slice(2, 10)}`;
|
|
164
|
+
|
|
165
|
+
// 3. Start a session -- this registers the device with the backend
|
|
166
|
+
await AppAnalytics.getInstance().startSession(deviceId, {
|
|
167
|
+
os: Platform.OS,
|
|
168
|
+
osVersion: String(Platform.Version),
|
|
169
|
+
model: 'Unknown', // Use react-native-device-info for real values
|
|
170
|
+
brand: 'Unknown',
|
|
171
|
+
screenWidth: width,
|
|
172
|
+
screenHeight: height,
|
|
173
|
+
appVersion: '1.0.0', // Replace with your app version
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
initialized = true;
|
|
177
|
+
if (__DEV__) console.log('[Repliqo] Session started');
|
|
178
|
+
} catch (error) {
|
|
179
|
+
// Never let analytics break the host app
|
|
180
|
+
if (__DEV__) console.warn('[Repliqo] Init failed:', error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Step 2: Call It from App.tsx
|
|
186
|
+
|
|
187
|
+
In your root `App.tsx`, import and call `initRepliqo()` inside a `useEffect`. Use a `setTimeout` to ensure analytics initialization never blocks your app's startup:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// App.tsx
|
|
191
|
+
|
|
192
|
+
import React, { useEffect } from 'react';
|
|
193
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
194
|
+
import { AppNavigator } from './src/navigation/AppNavigator';
|
|
195
|
+
import { initRepliqo } from './src/services/repliqo';
|
|
196
|
+
|
|
197
|
+
function App() {
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
// Initialize analytics after a short delay so it never blocks app startup
|
|
200
|
+
setTimeout(initRepliqo, 3000);
|
|
201
|
+
}, []);
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<SafeAreaProvider>
|
|
205
|
+
<AppNavigator />
|
|
206
|
+
</SafeAreaProvider>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export default App;
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
That is it. With these two files in place, the SDK will:
|
|
214
|
+
|
|
215
|
+
- Start a session with device metadata.
|
|
216
|
+
- Automatically flush events when the app goes to the background.
|
|
217
|
+
- Track navigation events and screen visits once you wire up the navigation tracker (see [Section 6](#6-navigation-tracking-automatic)).
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 5. Configuration Reference
|
|
222
|
+
|
|
223
|
+
All configuration is passed to `AppAnalytics.init()` via an `SDKConfig` object. The three required fields have no defaults and must always be provided. All other fields are optional and have sensible defaults.
|
|
224
|
+
|
|
225
|
+
| Property | Type | Default | Required | Description |
|
|
226
|
+
|---|---|---|---|---|
|
|
227
|
+
| `apiKey` | `string` | -- | Yes | Your Repliqo API key. Obtained from the dashboard under Settings > Applications. |
|
|
228
|
+
| `appId` | `string` | -- | Yes | Your application identifier. Must match the app ID registered in the dashboard. |
|
|
229
|
+
| `baseUrl` | `string` | -- | Yes | The Repliqo API base URL. Use `https://api-repliqo.odmservice.site` for production, or a local URL like `http://10.0.2.2:3050` for Android emulator development. |
|
|
230
|
+
| `batchSize` | `number` | `20` | No | Number of events to accumulate before sending a batch to the server. Lower values mean more frequent network requests; higher values reduce request count but increase the risk of data loss if the app is killed. |
|
|
231
|
+
| `flushInterval` | `number` | `30000` | No | Time in milliseconds between automatic batch flushes. Events are sent at least this often, even if `batchSize` has not been reached. Default is 30 seconds. |
|
|
232
|
+
| `enableTouchTracking` | `boolean` | `true` | No | When `true`, touch events (taps) are captured and sent to the backend for heatmap generation. Set to `false` if you only need navigation analytics. |
|
|
233
|
+
| `enableNavigationTracking` | `boolean` | `true` | No | When `true`, navigation events (screen transitions) are tracked. Works with `useNavigationTracker()` or `trackScreenChange()`. |
|
|
234
|
+
| `enableSnapshots` | `boolean` | `true` | No | When `true`, the SDK periodically captures a serialized snapshot of the component tree. Useful for session replay. Increases memory and bandwidth usage. |
|
|
235
|
+
| `enableCrashTracking` | `boolean` | `true` | No | When `true`, the SDK installs a global error handler and captures unhandled JavaScript errors and Promise rejections. Crash reports are sent immediately, not batched. |
|
|
236
|
+
| `snapshotInterval` | `number` | `1000` | No | Time in milliseconds between automatic snapshot captures. Only relevant when `enableSnapshots` is `true`. Default is 1 second. |
|
|
237
|
+
| `maxSnapshotsPerSession` | `number` | `3600` | No | Maximum number of snapshots the SDK will capture per session. Prevents runaway memory usage in long-lived sessions. At the default of 1 snapshot/second, this allows 1 hour of capture. |
|
|
238
|
+
| `debug` | `boolean` | `false` | No | When `true`, the SDK logs detailed information to the console (initialization, event tracking, flushes, errors). Recommended to set `debug: __DEV__` so logs only appear during development. |
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 6. Navigation Tracking (Automatic)
|
|
243
|
+
|
|
244
|
+
The SDK provides two approaches for automatic navigation tracking with `@react-navigation/native`. Choose the one that best fits your app's architecture.
|
|
245
|
+
|
|
246
|
+
### Approach A: Using the `useNavigationTracker()` Hook
|
|
247
|
+
|
|
248
|
+
The `useNavigationTracker()` hook is the cleanest integration method. It returns a `navigationRef`, an `onReady` callback, and an `onStateChange` callback that you pass directly to `NavigationContainer`.
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// src/navigation/AppNavigator.tsx
|
|
252
|
+
|
|
253
|
+
import React from 'react';
|
|
254
|
+
import { NavigationContainer } from '@react-navigation/native';
|
|
255
|
+
import { createStackNavigator } from '@react-navigation/stack';
|
|
256
|
+
import { useNavigationTracker } from '@repliqo/sdk-react-native';
|
|
257
|
+
import { HomeScreen } from '../screens/HomeScreen';
|
|
258
|
+
import { ProfileScreen } from '../screens/ProfileScreen';
|
|
259
|
+
import { SettingsScreen } from '../screens/SettingsScreen';
|
|
260
|
+
|
|
261
|
+
const Stack = createStackNavigator();
|
|
262
|
+
|
|
263
|
+
export function AppNavigator() {
|
|
264
|
+
// The hook manages all navigation tracking state internally
|
|
265
|
+
const { navigationRef, onReady, onStateChange } = useNavigationTracker();
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<NavigationContainer
|
|
269
|
+
ref={navigationRef}
|
|
270
|
+
onReady={onReady}
|
|
271
|
+
onStateChange={onStateChange}
|
|
272
|
+
>
|
|
273
|
+
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
|
274
|
+
<Stack.Screen name="Home" component={HomeScreen} />
|
|
275
|
+
<Stack.Screen name="Profile" component={ProfileScreen} />
|
|
276
|
+
<Stack.Screen name="Settings" component={SettingsScreen} />
|
|
277
|
+
</Stack.Navigator>
|
|
278
|
+
</NavigationContainer>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
When the navigator is ready, the hook records the initial screen. On every subsequent state change, it automatically:
|
|
284
|
+
|
|
285
|
+
1. Detects the currently active route (including nested navigators).
|
|
286
|
+
2. Calls `trackNavigation(fromScreen, toScreen)` to log the transition event.
|
|
287
|
+
3. Calls `onScreenExit(fromScreen)` to record how long the user spent on the previous screen.
|
|
288
|
+
4. Calls `onScreenEnter(toScreen)` to start the timer for the new screen.
|
|
289
|
+
|
|
290
|
+
### Approach B: Using `trackScreenChange()` Manually
|
|
291
|
+
|
|
292
|
+
If you need more control over how navigation state is read (for example, with complex nested navigators or non-standard setups), use the `trackScreenChange()` function directly:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// src/navigation/AppNavigator.tsx
|
|
296
|
+
|
|
297
|
+
import React, { useRef, useCallback } from 'react';
|
|
298
|
+
import { NavigationContainer } from '@react-navigation/native';
|
|
299
|
+
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
|
300
|
+
import { createStackNavigator } from '@react-navigation/stack';
|
|
301
|
+
import { trackScreenChange } from '@repliqo/sdk-react-native';
|
|
302
|
+
import { HomeScreen } from '../screens/HomeScreen';
|
|
303
|
+
import { ProfileScreen } from '../screens/ProfileScreen';
|
|
304
|
+
|
|
305
|
+
const Tab = createBottomTabNavigator();
|
|
306
|
+
const Stack = createStackNavigator();
|
|
307
|
+
|
|
308
|
+
// Helper to dig through nested navigators and find the active route name
|
|
309
|
+
function getActiveRouteName(state: any): string {
|
|
310
|
+
const route = state.routes[state.index];
|
|
311
|
+
if (route.state) return getActiveRouteName(route.state);
|
|
312
|
+
return route.name;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function AppNavigator() {
|
|
316
|
+
// Keep track of the current route name between renders
|
|
317
|
+
const currentRoute = useRef<string>('');
|
|
318
|
+
|
|
319
|
+
const onReady = useCallback(() => {
|
|
320
|
+
currentRoute.current = 'Home';
|
|
321
|
+
// Track the initial screen (fromScreen is empty on first load)
|
|
322
|
+
trackScreenChange('', 'Home');
|
|
323
|
+
}, []);
|
|
324
|
+
|
|
325
|
+
return (
|
|
326
|
+
<NavigationContainer
|
|
327
|
+
onReady={onReady}
|
|
328
|
+
onStateChange={(state) => {
|
|
329
|
+
if (!state) return;
|
|
330
|
+
|
|
331
|
+
const newRoute = getActiveRouteName(state);
|
|
332
|
+
if (newRoute !== currentRoute.current) {
|
|
333
|
+
// This single call handles navigation event + screen enter/exit
|
|
334
|
+
trackScreenChange(currentRoute.current, newRoute);
|
|
335
|
+
currentRoute.current = newRoute;
|
|
336
|
+
}
|
|
337
|
+
}}
|
|
338
|
+
>
|
|
339
|
+
<Tab.Navigator screenOptions={{ headerShown: false }}>
|
|
340
|
+
<Tab.Screen name="Home" component={HomeScreen} />
|
|
341
|
+
<Tab.Screen name="Profile" component={ProfileScreen} />
|
|
342
|
+
</Tab.Navigator>
|
|
343
|
+
</NavigationContainer>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### What Data Gets Captured
|
|
349
|
+
|
|
350
|
+
Each navigation event produces an `AnalyticsEvent` with this structure:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
{
|
|
354
|
+
type: 'navigation',
|
|
355
|
+
screenName: 'ProfileScreen', // The destination screen
|
|
356
|
+
data: {
|
|
357
|
+
fromScreen: 'HomeScreen', // Where the user came from
|
|
358
|
+
toScreen: 'ProfileScreen', // Where the user went
|
|
359
|
+
},
|
|
360
|
+
timestamp: '2026-03-25T14:30:00.000Z', // ISO 8601 timestamp
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Additionally, screen visits are tracked automatically with enter/exit timestamps and duration:
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
{
|
|
368
|
+
sessionId: 'abc123',
|
|
369
|
+
screenName: 'HomeScreen',
|
|
370
|
+
enteredAt: '2026-03-25T14:28:00.000Z',
|
|
371
|
+
exitedAt: '2026-03-25T14:30:00.000Z',
|
|
372
|
+
duration: 120000, // Duration in milliseconds
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## 7. Custom Events
|
|
379
|
+
|
|
380
|
+
Custom events let you track specific user actions that are meaningful to your business logic -- button clicks, form submissions, feature usage, purchase completions, and anything else you want to measure.
|
|
381
|
+
|
|
382
|
+
### Basic Usage
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
import { AppAnalytics } from '@repliqo/sdk-react-native';
|
|
386
|
+
|
|
387
|
+
// Track a simple event (name only)
|
|
388
|
+
AppAnalytics.getInstance().trackCustomEvent('sign_up_completed');
|
|
389
|
+
|
|
390
|
+
// Track an event with additional data
|
|
391
|
+
AppAnalytics.getInstance().trackCustomEvent('item_purchased', {
|
|
392
|
+
itemId: 'premium-plan',
|
|
393
|
+
price: 9.99,
|
|
394
|
+
currency: 'USD',
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Track feature usage
|
|
398
|
+
AppAnalytics.getInstance().trackCustomEvent('dark_mode_toggled', {
|
|
399
|
+
enabled: true,
|
|
400
|
+
});
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Real-World Example: Tracking Button Clicks
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// src/screens/HomeScreen.tsx
|
|
407
|
+
|
|
408
|
+
import React from 'react';
|
|
409
|
+
import { View, TouchableOpacity, Text } from 'react-native';
|
|
410
|
+
import { AppAnalytics } from '@repliqo/sdk-react-native';
|
|
411
|
+
|
|
412
|
+
export function HomeScreen() {
|
|
413
|
+
const handleCreateHabit = () => {
|
|
414
|
+
// Track the button click before performing the action
|
|
415
|
+
AppAnalytics.getInstance().trackCustomEvent('create_habit_tapped', {
|
|
416
|
+
source: 'home_screen',
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// ... your actual logic to create a habit
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const handleShareApp = () => {
|
|
423
|
+
AppAnalytics.getInstance().trackCustomEvent('share_app_tapped', {
|
|
424
|
+
source: 'home_screen',
|
|
425
|
+
method: 'native_share',
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// ... your share logic
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
return (
|
|
432
|
+
<View>
|
|
433
|
+
<TouchableOpacity onPress={handleCreateHabit}>
|
|
434
|
+
<Text>Create Habit</Text>
|
|
435
|
+
</TouchableOpacity>
|
|
436
|
+
<TouchableOpacity onPress={handleShareApp}>
|
|
437
|
+
<Text>Share App</Text>
|
|
438
|
+
</TouchableOpacity>
|
|
439
|
+
</View>
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Best Practices for Event Naming
|
|
445
|
+
|
|
446
|
+
- Use **snake_case** for event names: `purchase_completed`, not `PurchaseCompleted` or `purchase-completed`.
|
|
447
|
+
- Start with a **noun** followed by a **verb in past tense**: `habit_created`, `profile_updated`, `session_started`.
|
|
448
|
+
- Keep names **short but descriptive**: `checkout_started` is better than `user_clicked_checkout_button`.
|
|
449
|
+
- Use the `data` parameter for **variable information**: pass `itemId`, `price`, `category` as data, not as part of the event name.
|
|
450
|
+
- Avoid **high-cardinality event names**: do not include IDs or dynamic values in the event name itself. Use `item_viewed` with `{ itemId: '123' }`, not `item_123_viewed`.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## 8. Error/Crash Tracking
|
|
455
|
+
|
|
456
|
+
The SDK can automatically capture JavaScript errors, fatal native crashes, and unhandled Promise rejections, and send them to the Repliqo backend for analysis.
|
|
457
|
+
|
|
458
|
+
### Automatic Crash Capture
|
|
459
|
+
|
|
460
|
+
When `enableCrashTracking` is set to `true` (the default), the SDK installs:
|
|
461
|
+
|
|
462
|
+
1. **A global error handler** via React Native's `ErrorUtils`. This captures all uncaught JavaScript errors, including fatal errors that would crash the app.
|
|
463
|
+
2. **An unhandled rejection handler** that captures Promise rejections that are not caught by a `.catch()` block.
|
|
464
|
+
|
|
465
|
+
Crash reports are sent **immediately** when they occur (not batched), because the app may be about to terminate. The original React Native error handler is preserved and still called, so the standard red screen / crash behavior is unaffected.
|
|
466
|
+
|
|
467
|
+
No additional setup is needed beyond enabling the flag:
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
AppAnalytics.init({
|
|
471
|
+
apiKey: 'your-api-key',
|
|
472
|
+
appId: 'your-app-id',
|
|
473
|
+
baseUrl: 'https://api-repliqo.odmservice.site',
|
|
474
|
+
enableCrashTracking: true, // This is the default
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Manual Error Reporting
|
|
479
|
+
|
|
480
|
+
For errors that you catch yourself (e.g., in try/catch blocks, error boundaries, or API call failures), use `reportError()`:
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
import { AppAnalytics } from '@repliqo/sdk-react-native';
|
|
484
|
+
|
|
485
|
+
// Report a caught error with context metadata
|
|
486
|
+
async function fetchUserProfile(userId: string) {
|
|
487
|
+
try {
|
|
488
|
+
const response = await fetch(`/api/users/${userId}`);
|
|
489
|
+
if (!response.ok) {
|
|
490
|
+
throw new Error(`HTTP ${response.status}: Failed to fetch profile`);
|
|
491
|
+
}
|
|
492
|
+
return await response.json();
|
|
493
|
+
} catch (error) {
|
|
494
|
+
// Report to Repliqo with extra context
|
|
495
|
+
AppAnalytics.getInstance().reportError(error as Error, {
|
|
496
|
+
userId,
|
|
497
|
+
action: 'fetch_user_profile',
|
|
498
|
+
httpStatus: (error as any).status,
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Handle the error in the UI as normal
|
|
502
|
+
throw error;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### React Error Boundary Example
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
// src/components/AnalyticsErrorBoundary.tsx
|
|
511
|
+
|
|
512
|
+
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
513
|
+
import { View, Text } from 'react-native';
|
|
514
|
+
import { AppAnalytics } from '@repliqo/sdk-react-native';
|
|
515
|
+
|
|
516
|
+
interface Props {
|
|
517
|
+
children: ReactNode;
|
|
518
|
+
fallback?: ReactNode;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
interface State {
|
|
522
|
+
hasError: boolean;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export class AnalyticsErrorBoundary extends Component<Props, State> {
|
|
526
|
+
state: State = { hasError: false };
|
|
527
|
+
|
|
528
|
+
static getDerivedStateFromError(): State {
|
|
529
|
+
return { hasError: true };
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
533
|
+
// Report the error to Repliqo with component stack information
|
|
534
|
+
try {
|
|
535
|
+
AppAnalytics.getInstance().reportError(error, {
|
|
536
|
+
componentStack: errorInfo.componentStack,
|
|
537
|
+
source: 'error_boundary',
|
|
538
|
+
});
|
|
539
|
+
} catch {
|
|
540
|
+
// SDK may not be initialized yet
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
render() {
|
|
545
|
+
if (this.state.hasError) {
|
|
546
|
+
return this.props.fallback || (
|
|
547
|
+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
|
548
|
+
<Text>Something went wrong.</Text>
|
|
549
|
+
</View>
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
return this.props.children;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### What Data Is Captured in a Crash Report
|
|
558
|
+
|
|
559
|
+
Each crash report contains the following fields:
|
|
560
|
+
|
|
561
|
+
| Field | Type | Description |
|
|
562
|
+
|---|---|---|
|
|
563
|
+
| `sessionId` | `string` or `undefined` | The active session ID, if a session is running. |
|
|
564
|
+
| `type` | `'js_error'` \| `'native_crash'` \| `'unhandled_rejection'` | The category of error. Fatal errors from `ErrorUtils` are reported as `native_crash`. |
|
|
565
|
+
| `message` | `string` | The error message (e.g., `"Cannot read property 'name' of undefined"`). |
|
|
566
|
+
| `stackTrace` | `string` or `undefined` | The JavaScript stack trace, if available. |
|
|
567
|
+
| `screenName` | `string` or `undefined` | The screen the user was on when the crash occurred. |
|
|
568
|
+
| `deviceInfo` | `Record<string, any>` or `undefined` | Optional device context. |
|
|
569
|
+
| `metadata` | `Record<string, any>` or `undefined` | Any additional metadata you pass to `reportError()`. For automatic captures, this includes `{ isFatal: boolean }`. |
|
|
570
|
+
| `timestamp` | `string` | ISO 8601 timestamp of when the error occurred. |
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## 9. Manual Screen Tracking
|
|
575
|
+
|
|
576
|
+
In most cases, the automatic navigation tracker (Section 6) handles screen tracking for you. However, there are scenarios where you need manual control:
|
|
577
|
+
|
|
578
|
+
- Screens not managed by React Navigation (e.g., modals, bottom sheets, onboarding flows).
|
|
579
|
+
- Custom navigation systems that do not use `@react-navigation/native`.
|
|
580
|
+
- Conditional screens that appear within a single navigator route.
|
|
581
|
+
|
|
582
|
+
### Using `trackScreenEnter()` and `trackScreenExit()`
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
import { trackScreenEnter, trackScreenExit } from '@repliqo/sdk-react-native';
|
|
586
|
+
|
|
587
|
+
// Call when the screen becomes visible
|
|
588
|
+
trackScreenEnter('OnboardingStep1');
|
|
589
|
+
|
|
590
|
+
// Call when the screen is no longer visible
|
|
591
|
+
trackScreenExit('OnboardingStep1');
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Example: Tracking a Modal Screen
|
|
595
|
+
|
|
596
|
+
```typescript
|
|
597
|
+
// src/components/PremiumModal.tsx
|
|
598
|
+
|
|
599
|
+
import React, { useEffect } from 'react';
|
|
600
|
+
import { Modal, View, Text } from 'react-native';
|
|
601
|
+
import { trackScreenEnter, trackScreenExit } from '@repliqo/sdk-react-native';
|
|
602
|
+
|
|
603
|
+
interface PremiumModalProps {
|
|
604
|
+
visible: boolean;
|
|
605
|
+
onClose: () => void;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export function PremiumModal({ visible, onClose }: PremiumModalProps) {
|
|
609
|
+
useEffect(() => {
|
|
610
|
+
if (visible) {
|
|
611
|
+
// Register this modal as a screen visit when it opens
|
|
612
|
+
trackScreenEnter('PremiumUpgradeModal');
|
|
613
|
+
} else {
|
|
614
|
+
// Record the exit (and duration) when it closes
|
|
615
|
+
trackScreenExit('PremiumUpgradeModal');
|
|
616
|
+
}
|
|
617
|
+
}, [visible]);
|
|
618
|
+
|
|
619
|
+
return (
|
|
620
|
+
<Modal visible={visible} onRequestClose={onClose}>
|
|
621
|
+
<View>
|
|
622
|
+
<Text>Upgrade to Premium</Text>
|
|
623
|
+
{/* ... modal content ... */}
|
|
624
|
+
</View>
|
|
625
|
+
</Modal>
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### When to Use Manual vs. Automatic
|
|
631
|
+
|
|
632
|
+
| Scenario | Recommended Approach |
|
|
633
|
+
|---|---|
|
|
634
|
+
| Standard React Navigation stack/tab screens | Automatic (`useNavigationTracker` or `trackScreenChange`) |
|
|
635
|
+
| Modals and bottom sheets | Manual (`trackScreenEnter` / `trackScreenExit`) |
|
|
636
|
+
| Onboarding wizard steps within a single route | Manual |
|
|
637
|
+
| Custom navigation (no React Navigation) | Manual |
|
|
638
|
+
| WebView content within a native screen | Manual (track the WebView "page" as a screen) |
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## 10. Session Management
|
|
643
|
+
|
|
644
|
+
A session represents a single continuous usage period of your app by a user. Sessions tie all events, screen visits, and crash reports together.
|
|
645
|
+
|
|
646
|
+
### How Sessions Work
|
|
647
|
+
|
|
648
|
+
1. **Start**: You call `startSession()` with a device identifier and optional device info. The backend creates a session record and returns a session ID.
|
|
649
|
+
2. **Track**: While the session is active, all events (navigation, touch, custom, screen visits) are associated with this session ID.
|
|
650
|
+
3. **Flush**: Events are periodically flushed to the backend in batches. An automatic flush also happens when the app goes to the background.
|
|
651
|
+
4. **End**: You call `endSession()` (or let the SDK handle it on destroy). This flushes remaining events and tells the backend the session is complete.
|
|
652
|
+
|
|
653
|
+
### Starting a Session
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
import { AppAnalytics } from '@repliqo/sdk-react-native';
|
|
657
|
+
import { Platform, Dimensions } from 'react-native';
|
|
658
|
+
|
|
659
|
+
const analytics = AppAnalytics.getInstance();
|
|
660
|
+
|
|
661
|
+
// Generate or retrieve a persistent device ID
|
|
662
|
+
const deviceId = 'unique-device-identifier';
|
|
663
|
+
|
|
664
|
+
// Gather device info (see Section 11 for a complete example)
|
|
665
|
+
const { width, height } = Dimensions.get('window');
|
|
666
|
+
const deviceInfo = {
|
|
667
|
+
os: Platform.OS,
|
|
668
|
+
osVersion: String(Platform.Version),
|
|
669
|
+
model: 'Pixel 7',
|
|
670
|
+
brand: 'Google',
|
|
671
|
+
screenWidth: width,
|
|
672
|
+
screenHeight: height,
|
|
673
|
+
appVersion: '2.1.0',
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
// startSession returns the session ID assigned by the backend
|
|
677
|
+
const sessionId = await analytics.startSession(deviceId, deviceInfo);
|
|
678
|
+
console.log('Session ID:', sessionId);
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Ending a Session
|
|
682
|
+
|
|
683
|
+
```typescript
|
|
684
|
+
// End the session -- this flushes all pending events first
|
|
685
|
+
await AppAnalytics.getInstance().endSession();
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
Ending a session:
|
|
689
|
+
|
|
690
|
+
1. Stops snapshot capture (if enabled).
|
|
691
|
+
2. Records the exit for the current screen (if the user is on one).
|
|
692
|
+
3. Flushes all queued events, screen visits, and snapshots.
|
|
693
|
+
4. Notifies the backend that the session has ended.
|
|
694
|
+
5. Clears the session ID and current screen state.
|
|
695
|
+
|
|
696
|
+
### Checking Session State
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
const analytics = AppAnalytics.getInstance();
|
|
700
|
+
|
|
701
|
+
// Get the current session ID (null if no session is active)
|
|
702
|
+
const sessionId = analytics.getSessionId();
|
|
703
|
+
|
|
704
|
+
// Get the name of the screen the user is currently on
|
|
705
|
+
const currentScreen = analytics.getCurrentScreen();
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
### Session Lifecycle Diagram
|
|
709
|
+
|
|
710
|
+
```
|
|
711
|
+
App Launch
|
|
712
|
+
|
|
|
713
|
+
v
|
|
714
|
+
AppAnalytics.init(config) --> SDK initialized, queues created
|
|
715
|
+
|
|
|
716
|
+
v
|
|
717
|
+
analytics.startSession() --> Backend creates session, returns ID
|
|
718
|
+
|
|
|
719
|
+
v
|
|
720
|
+
[User interacts with app] --> Events buffered in memory
|
|
721
|
+
|
|
|
722
|
+
v
|
|
723
|
+
[Batch threshold reached] --> Events sent to backend
|
|
724
|
+
| (automatic, every flushInterval or batchSize)
|
|
725
|
+
v
|
|
726
|
+
[App goes to background] --> Automatic flush triggered
|
|
727
|
+
|
|
|
728
|
+
v
|
|
729
|
+
[App returns to foreground] --> Tracking resumes (same session)
|
|
730
|
+
|
|
|
731
|
+
v
|
|
732
|
+
analytics.endSession() --> Final flush, session closed on backend
|
|
733
|
+
|
|
|
734
|
+
v
|
|
735
|
+
AppAnalytics.destroy() --> Cleanup, listeners removed
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
---
|
|
739
|
+
|
|
740
|
+
## 11. Device Info
|
|
741
|
+
|
|
742
|
+
The `DeviceInfo` object provides context about the user's device with each session. This data appears in the dashboard alongside session details and crash reports.
|
|
743
|
+
|
|
744
|
+
### Basic Device Info (No Extra Dependencies)
|
|
745
|
+
|
|
746
|
+
Using only React Native's built-in APIs:
|
|
747
|
+
|
|
748
|
+
```typescript
|
|
749
|
+
import { Platform, Dimensions } from 'react-native';
|
|
750
|
+
import type { DeviceInfo } from '@repliqo/sdk-react-native';
|
|
751
|
+
|
|
752
|
+
function getBasicDeviceInfo(): DeviceInfo {
|
|
753
|
+
const { width, height } = Dimensions.get('window');
|
|
754
|
+
|
|
755
|
+
return {
|
|
756
|
+
os: Platform.OS, // 'ios' or 'android'
|
|
757
|
+
osVersion: String(Platform.Version), // e.g., '33' (Android) or '17.2' (iOS)
|
|
758
|
+
model: 'Unknown', // Not available without a library
|
|
759
|
+
brand: 'Unknown', // Not available without a library
|
|
760
|
+
screenWidth: width, // e.g., 393
|
|
761
|
+
screenHeight: height, // e.g., 852
|
|
762
|
+
appVersion: '1.0.0', // Hardcode or read from your config
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### Enhanced Device Info with `react-native-device-info`
|
|
768
|
+
|
|
769
|
+
For richer device data, install [react-native-device-info](https://github.com/react-native-device-info/react-native-device-info):
|
|
770
|
+
|
|
771
|
+
```bash
|
|
772
|
+
npm install react-native-device-info
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
Then use it to populate the fields:
|
|
776
|
+
|
|
777
|
+
```typescript
|
|
778
|
+
import { Platform, Dimensions } from 'react-native';
|
|
779
|
+
import DeviceInfoLib from 'react-native-device-info';
|
|
780
|
+
import type { DeviceInfo } from '@repliqo/sdk-react-native';
|
|
781
|
+
|
|
782
|
+
async function getEnhancedDeviceInfo(): Promise<DeviceInfo> {
|
|
783
|
+
const { width, height } = Dimensions.get('window');
|
|
784
|
+
|
|
785
|
+
return {
|
|
786
|
+
os: Platform.OS,
|
|
787
|
+
osVersion: String(Platform.Version),
|
|
788
|
+
model: DeviceInfoLib.getModel(), // e.g., 'Pixel 7'
|
|
789
|
+
brand: DeviceInfoLib.getBrand(), // e.g., 'Google'
|
|
790
|
+
screenWidth: width,
|
|
791
|
+
screenHeight: height,
|
|
792
|
+
appVersion: DeviceInfoLib.getVersion(), // e.g., '2.1.0'
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Generating a Persistent Device ID
|
|
798
|
+
|
|
799
|
+
For the `deviceId` parameter in `startSession()`, you need a value that is unique per device and persists across app launches. Here are some approaches:
|
|
800
|
+
|
|
801
|
+
```typescript
|
|
802
|
+
import DeviceInfoLib from 'react-native-device-info';
|
|
803
|
+
|
|
804
|
+
// Option 1: Use react-native-device-info's unique ID (recommended)
|
|
805
|
+
const deviceId = await DeviceInfoLib.getUniqueId();
|
|
806
|
+
|
|
807
|
+
// Option 2: Generate a random ID and store it with AsyncStorage
|
|
808
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
809
|
+
|
|
810
|
+
async function getOrCreateDeviceId(): Promise<string> {
|
|
811
|
+
let deviceId = await AsyncStorage.getItem('repliqo_device_id');
|
|
812
|
+
if (!deviceId) {
|
|
813
|
+
deviceId = `${Platform.OS}-${Math.random().toString(36).slice(2, 10)}`;
|
|
814
|
+
await AsyncStorage.setItem('repliqo_device_id', deviceId);
|
|
815
|
+
}
|
|
816
|
+
return deviceId;
|
|
817
|
+
}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
---
|
|
821
|
+
|
|
822
|
+
## 12. Advanced: Flush Control
|
|
823
|
+
|
|
824
|
+
The SDK batches events in memory and sends them to the backend periodically. You can control when flushes happen.
|
|
825
|
+
|
|
826
|
+
### Automatic Flushing
|
|
827
|
+
|
|
828
|
+
By default, the SDK flushes automatically in two situations:
|
|
829
|
+
|
|
830
|
+
1. **Timer-based**: Every `flushInterval` milliseconds (default: 30,000ms), all queued events are sent.
|
|
831
|
+
2. **App background**: When the app transitions to the `background` or `inactive` state, a flush is triggered immediately. This is handled internally by the SDK via an `AppState` listener and requires no action from you.
|
|
832
|
+
|
|
833
|
+
### Manual Flushing
|
|
834
|
+
|
|
835
|
+
You can trigger a flush at any time:
|
|
836
|
+
|
|
837
|
+
```typescript
|
|
838
|
+
import { AppAnalytics } from '@repliqo/sdk-react-native';
|
|
839
|
+
|
|
840
|
+
// Flush all pending events, screen visits, and snapshots immediately
|
|
841
|
+
await AppAnalytics.getInstance().flush();
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
This is useful in scenarios like:
|
|
845
|
+
|
|
846
|
+
- Before navigating to a payment screen (ensure all events are sent).
|
|
847
|
+
- After a critical user action that you want to see in the dashboard right away.
|
|
848
|
+
- In a "log out" flow before ending the session.
|
|
849
|
+
|
|
850
|
+
### Tuning `batchSize` and `flushInterval`
|
|
851
|
+
|
|
852
|
+
| Use Case | Recommended `batchSize` | Recommended `flushInterval` |
|
|
853
|
+
|---|---|---|
|
|
854
|
+
| **Low-traffic app** (few screens, few events) | `10` | `60000` (60s) |
|
|
855
|
+
| **Standard app** | `20` (default) | `30000` (30s, default) |
|
|
856
|
+
| **High-traffic app** (many touch events, frequent navigation) | `50` | `15000` (15s) |
|
|
857
|
+
| **Real-time monitoring** (debugging, demo) | `5` | `5000` (5s) |
|
|
858
|
+
|
|
859
|
+
Lower `batchSize` and `flushInterval` values mean more frequent network requests but lower risk of data loss. Higher values reduce network usage but events take longer to appear in the dashboard.
|
|
860
|
+
|
|
861
|
+
---
|
|
862
|
+
|
|
863
|
+
## 13. Advanced: Debug Mode
|
|
864
|
+
|
|
865
|
+
Debug mode enables verbose console logging from the SDK, which is essential during development and integration testing.
|
|
866
|
+
|
|
867
|
+
### Enabling Debug Mode
|
|
868
|
+
|
|
869
|
+
```typescript
|
|
870
|
+
AppAnalytics.init({
|
|
871
|
+
apiKey: 'your-api-key',
|
|
872
|
+
appId: 'your-app-id',
|
|
873
|
+
baseUrl: 'https://api-repliqo.odmservice.site',
|
|
874
|
+
debug: true, // Enable verbose logging
|
|
875
|
+
});
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
A common pattern is to tie debug mode to the development build:
|
|
879
|
+
|
|
880
|
+
```typescript
|
|
881
|
+
debug: __DEV__, // true in development, false in production
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
### What Logs to Expect
|
|
885
|
+
|
|
886
|
+
With debug mode enabled, you will see console output for every SDK operation:
|
|
887
|
+
|
|
888
|
+
```
|
|
889
|
+
[Repliqo] SDK initialized with config: { baseUrl: "...", appId: "myapp", batchSize: 20, ... }
|
|
890
|
+
[Repliqo] Session started with ID: sess_abc123def456
|
|
891
|
+
[Repliqo] Screen entered: HomeScreen
|
|
892
|
+
[Repliqo] Navigation tracked: HomeScreen -> ProfileScreen
|
|
893
|
+
[Repliqo] Screen exited: HomeScreen duration: 4523
|
|
894
|
+
[Repliqo] Screen entered: ProfileScreen
|
|
895
|
+
[Repliqo] Custom event tracked: settings_opened
|
|
896
|
+
[Repliqo] Touch tracked: { type: "touch", screenName: "ProfileScreen", data: { x: 195, y: 420 } }
|
|
897
|
+
[Repliqo] Flushing queues...
|
|
898
|
+
[Repliqo] Flush complete
|
|
899
|
+
[Repliqo] App going to background/inactive, flushing queues
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
### How to Verify Events Are Being Sent
|
|
903
|
+
|
|
904
|
+
1. **Enable debug mode** and watch the console for "Flushing queues..." followed by "Flush complete" without errors.
|
|
905
|
+
2. **Check the dashboard**: Go to [https://repliqo.odmservice.site](https://repliqo.odmservice.site) and look for your session in the Sessions list. Events should appear within a few seconds of a flush.
|
|
906
|
+
3. **Monitor network requests**: Use React Native Debugger or Flipper to inspect outgoing HTTP requests to your `baseUrl`. You should see POST requests to endpoints like `/sessions`, `/events/batch`, and `/screen-visits/batch`.
|
|
907
|
+
4. **Force a flush**: Call `AppAnalytics.getInstance().flush()` from a debug button in your app to send all queued events immediately.
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
## 14. Dashboard Guide
|
|
912
|
+
|
|
913
|
+
The Repliqo dashboard at [https://repliqo.odmservice.site](https://repliqo.odmservice.site) provides a visual interface for all the data your SDK collects. Here is a brief overview of the main sections.
|
|
914
|
+
|
|
915
|
+
### Sessions
|
|
916
|
+
|
|
917
|
+
The sessions view lists all recorded user sessions with:
|
|
918
|
+
|
|
919
|
+
- **Device info**: OS, OS version, device model, brand, screen dimensions.
|
|
920
|
+
- **Duration**: How long the session lasted.
|
|
921
|
+
- **Screen count**: Number of screens visited during the session.
|
|
922
|
+
- **Event count**: Total events tracked (navigation, touch, custom).
|
|
923
|
+
- **Timestamps**: Start and end times.
|
|
924
|
+
|
|
925
|
+
Click on a session to see its full timeline of events.
|
|
926
|
+
|
|
927
|
+
### Screen Visit Analytics
|
|
928
|
+
|
|
929
|
+
View aggregate data about which screens users visit most often:
|
|
930
|
+
|
|
931
|
+
- **Screen popularity**: Ranked list of screens by visit count.
|
|
932
|
+
- **Average duration**: How long users spend on each screen.
|
|
933
|
+
- **Entry/exit points**: Which screens users land on first and leave from.
|
|
934
|
+
|
|
935
|
+
### Heatmaps
|
|
936
|
+
|
|
937
|
+
When touch tracking is enabled, the dashboard generates visual heatmaps showing where users tap on each screen:
|
|
938
|
+
|
|
939
|
+
- **Tap density**: Color-coded overlay showing hotspots of user interaction.
|
|
940
|
+
- **Per-screen breakdown**: View heatmaps for individual screens.
|
|
941
|
+
- **Session-level replay**: See the exact touch sequence for a specific session.
|
|
942
|
+
|
|
943
|
+
### Crash Reports
|
|
944
|
+
|
|
945
|
+
The crash reports section aggregates all errors reported by the SDK:
|
|
946
|
+
|
|
947
|
+
- **Error grouping**: Similar errors are grouped together by message and stack trace.
|
|
948
|
+
- **Occurrence count**: How many times each error has occurred.
|
|
949
|
+
- **Affected sessions**: Which sessions experienced the error.
|
|
950
|
+
- **Stack traces**: Full JavaScript stack traces for debugging.
|
|
951
|
+
- **Context**: The screen the user was on, device info, and any metadata you attached.
|
|
952
|
+
|
|
953
|
+
### Funnels
|
|
954
|
+
|
|
955
|
+
Define multi-step funnels to track conversion through specific user flows:
|
|
956
|
+
|
|
957
|
+
- Configure a sequence of screens or events.
|
|
958
|
+
- See drop-off rates at each step.
|
|
959
|
+
- Compare funnel performance across time periods.
|
|
960
|
+
|
|
961
|
+
### Data Export
|
|
962
|
+
|
|
963
|
+
Export your analytics data for use in external tools:
|
|
964
|
+
|
|
965
|
+
- Export sessions, events, or crash reports as CSV or JSON.
|
|
966
|
+
- Filter by date range, app version, or device type before exporting.
|
|
967
|
+
|
|
968
|
+
---
|
|
969
|
+
|
|
970
|
+
## 15. API Reference
|
|
971
|
+
|
|
972
|
+
Quick reference for all public methods on the `AppAnalytics` class.
|
|
973
|
+
|
|
974
|
+
### Static Methods
|
|
975
|
+
|
|
976
|
+
| Method | Signature | Returns | Description |
|
|
977
|
+
|---|---|---|---|
|
|
978
|
+
| `init` | `init(config: SDKConfig)` | `AppAnalytics` | Initializes the SDK with the given configuration. Returns the singleton instance. Safe to call multiple times (subsequent calls return the existing instance). |
|
|
979
|
+
| `getInstance` | `getInstance()` | `AppAnalytics` | Returns the singleton instance. Throws an error if `init()` has not been called. |
|
|
980
|
+
| `destroy` | `destroy()` | `void` | Tears down the SDK: stops error tracking and snapshot capture, stops auto-flush timers, performs a final flush (fire-and-forget), removes the AppState listener, and nullifies the singleton. |
|
|
981
|
+
|
|
982
|
+
### Instance Methods
|
|
983
|
+
|
|
984
|
+
| Method | Signature | Returns | Description |
|
|
985
|
+
|---|---|---|---|
|
|
986
|
+
| `startSession` | `startSession(deviceId: string, deviceInfo?: DeviceInfo)` | `Promise<string>` | Starts a new session with the backend. Returns the session ID. Sets up error tracker and snapshot capture for this session. |
|
|
987
|
+
| `endSession` | `endSession()` | `Promise<void>` | Ends the current session. Flushes all pending data, records the final screen exit, and notifies the backend. |
|
|
988
|
+
| `trackTouch` | `trackTouch(x: number, y: number, screenName?: string, extra?: Record<string, any>)` | `void` | Records a touch event at the given coordinates. If `screenName` is omitted, uses the current screen. Only works when `enableTouchTracking` is `true`. |
|
|
989
|
+
| `trackNavigation` | `trackNavigation(fromScreen: string, toScreen: string)` | `void` | Records a navigation event between two screens. Only works when `enableNavigationTracking` is `true`. |
|
|
990
|
+
| `trackCustomEvent` | `trackCustomEvent(eventName: string, data?: Record<string, any>)` | `void` | Records a custom event with an arbitrary name and optional data payload. |
|
|
991
|
+
| `reportError` | `reportError(error: Error, metadata?: Record<string, any>)` | `void` | Manually reports a caught error. Only works when `enableCrashTracking` is `true`. The error is sent immediately, not batched. |
|
|
992
|
+
| `onScreenEnter` | `onScreenEnter(screenName: string)` | `void` | Marks the user as having entered a screen. Starts the visit timer. Updates the error tracker's current screen context. |
|
|
993
|
+
| `onScreenExit` | `onScreenExit(screenName: string)` | `void` | Marks the user as having exited a screen. Records a `ScreenVisit` with the duration in the screen visit queue. |
|
|
994
|
+
| `captureSnapshot` | `captureSnapshot(tree: ComponentNode, screenName?: string)` | `void` | Manually captures a component tree snapshot. Only works when `enableSnapshots` is `true` and a session is active. |
|
|
995
|
+
| `flush` | `flush()` | `Promise<void>` | Immediately sends all queued events, screen visits, and snapshots to the backend. |
|
|
996
|
+
| `getSessionId` | `getSessionId()` | `string \| null` | Returns the current session ID, or `null` if no session is active. |
|
|
997
|
+
| `getCurrentScreen` | `getCurrentScreen()` | `string \| null` | Returns the name of the screen the user is currently on, or `null`. |
|
|
998
|
+
| `isInitialized` | `isInitialized()` | `boolean` | Returns `true` if the SDK instance exists and is ready. |
|
|
999
|
+
|
|
1000
|
+
### Standalone Functions
|
|
1001
|
+
|
|
1002
|
+
| Function | Signature | Description |
|
|
1003
|
+
|---|---|---|
|
|
1004
|
+
| `useNavigationTracker` | `useNavigationTracker()` | React hook that returns `{ navigationRef, onReady, onStateChange }` for automatic navigation tracking with `NavigationContainer`. |
|
|
1005
|
+
| `trackScreenChange` | `trackScreenChange(fromScreen: string, toScreen: string)` | Convenience function that calls `trackNavigation`, `onScreenExit`, and `onScreenEnter` in sequence. |
|
|
1006
|
+
| `trackScreenEnter` | `trackScreenEnter(screenName: string)` | Standalone function to mark screen entry. Silently no-ops if SDK is not initialized. |
|
|
1007
|
+
| `trackScreenExit` | `trackScreenExit(screenName: string)` | Standalone function to mark screen exit. Silently no-ops if SDK is not initialized. |
|
|
1008
|
+
|
|
1009
|
+
---
|
|
1010
|
+
|
|
1011
|
+
## 16. Screen Capture Architecture
|
|
1012
|
+
|
|
1013
|
+
The SDK uses a **two-tier capture strategy** for session replay screenshots:
|
|
1014
|
+
|
|
1015
|
+
### Tier 1: Native Multi-Window Capture (Android)
|
|
1016
|
+
|
|
1017
|
+
The SDK includes a native Android module (`ScreenCaptureModule`) that captures **all visible app windows** — including modals, alerts, system dialogs, keyboard overlays, and toasts.
|
|
1018
|
+
|
|
1019
|
+
**How it works:**
|
|
1020
|
+
- Uses `WindowManagerGlobal` (via reflection) to enumerate all root views in the process
|
|
1021
|
+
- Draws each visible view to a shared `Canvas` using `View.draw()`, composited in z-order
|
|
1022
|
+
- Scales to 390px width, compresses to JPEG at 40% quality
|
|
1023
|
+
- Returns base64-encoded JPEG to JavaScript
|
|
1024
|
+
|
|
1025
|
+
**No permissions required.** No `MediaProjection`, no foreground service, no system dialogs.
|
|
1026
|
+
|
|
1027
|
+
### Tier 2: react-native-view-shot (Fallback)
|
|
1028
|
+
|
|
1029
|
+
If the native module is unavailable (iOS, or if the module fails to load), the SDK falls back to `react-native-view-shot`'s `captureScreen()`. This captures only the main React Native view — **modals and native dialogs are NOT included** in the fallback.
|
|
1030
|
+
|
|
1031
|
+
### Capture Priority
|
|
1032
|
+
|
|
1033
|
+
```
|
|
1034
|
+
captureScreenshot()
|
|
1035
|
+
├─ Try: Native multi-window capture (Android)
|
|
1036
|
+
│ └─ Returns: full screenshot including modals/alerts
|
|
1037
|
+
├─ Fallback: react-native-view-shot
|
|
1038
|
+
│ └─ Returns: main view only (no modals)
|
|
1039
|
+
└─ Last resort: null (frame dropped)
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
Each frame includes a `source` field (`'native'` or `'viewshot'`) so the replay player knows which method produced it.
|
|
1043
|
+
|
|
1044
|
+
### iOS Support
|
|
1045
|
+
|
|
1046
|
+
Native multi-window capture is not yet implemented for iOS. iOS sessions use `react-native-view-shot` exclusively. This is a known limitation documented for future development.
|
|
1047
|
+
|
|
1048
|
+
---
|
|
1049
|
+
|
|
1050
|
+
## 17. Troubleshooting
|
|
1051
|
+
|
|
1052
|
+
### "Failed to start session"
|
|
1053
|
+
|
|
1054
|
+
**Symptom**: `startSession()` throws an error or the console shows `[Repliqo] Init failed`.
|
|
1055
|
+
|
|
1056
|
+
**Possible causes and fixes**:
|
|
1057
|
+
|
|
1058
|
+
- **Wrong `baseUrl`**: Verify the URL is correct. For Android emulator, use `http://10.0.2.2:<port>` (not `localhost`). For iOS simulator, use `http://localhost:<port>`.
|
|
1059
|
+
- **Invalid API key**: Double-check your `apiKey` matches the one in the Repliqo dashboard.
|
|
1060
|
+
- **Network issue**: Ensure the device/emulator has network access. Try opening the `baseUrl` in a browser.
|
|
1061
|
+
- **Backend not running**: If developing locally, confirm the Repliqo API server is running.
|
|
1062
|
+
|
|
1063
|
+
### Events Not Appearing in the Dashboard
|
|
1064
|
+
|
|
1065
|
+
**Symptom**: You see "Session started" in the logs, but events do not show up in the dashboard.
|
|
1066
|
+
|
|
1067
|
+
**Possible causes and fixes**:
|
|
1068
|
+
|
|
1069
|
+
- **Events are still queued**: The SDK buffers events and sends them in batches. Wait for the `flushInterval` (default 30s) or call `flush()` manually.
|
|
1070
|
+
- **`batchSize` too large**: If your `batchSize` is set very high and users don't generate that many events, the batch may never fill up before the flush interval. Reduce `batchSize` or `flushInterval`.
|
|
1071
|
+
- **Session not started**: Events are silently dropped if no session is active. Check that `startSession()` completed successfully.
|
|
1072
|
+
- **Feature disabled**: If `enableNavigationTracking` is `false`, navigation events are silently ignored. Same for `enableTouchTracking` and touch events.
|
|
1073
|
+
|
|
1074
|
+
### Metro Can't Resolve the Module
|
|
1075
|
+
|
|
1076
|
+
**Symptom**: Error like `Unable to resolve module @repliqo/sdk-react-native`.
|
|
1077
|
+
|
|
1078
|
+
**Fix**: Clear the Metro bundler cache:
|
|
1079
|
+
|
|
1080
|
+
```bash
|
|
1081
|
+
npx react-native start --reset-cache
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
If installing from a local path, make sure the path in `package.json` is correct and that the SDK has been built:
|
|
1085
|
+
|
|
1086
|
+
```bash
|
|
1087
|
+
cd /path/to/sdk-react-native
|
|
1088
|
+
npm run build
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
### Touch Events Not Working
|
|
1092
|
+
|
|
1093
|
+
**Symptom**: Touch tracking is enabled but no touch events appear.
|
|
1094
|
+
|
|
1095
|
+
**Possible causes and fixes**:
|
|
1096
|
+
|
|
1097
|
+
- **`enableTouchTracking: false`**: Verify this is set to `true` in your config.
|
|
1098
|
+
- **TouchTracker wrapper missing**: The `TouchTracker` component must wrap your app's component tree to intercept touch events. If you are not using it, touches will not be captured. Consider using manual `trackTouch()` calls as an alternative.
|
|
1099
|
+
- **Known limitation**: The `TouchTracker` component can sometimes interfere with gesture handlers (e.g., `react-native-gesture-handler`). If you experience issues, disable `enableTouchTracking` and use manual `trackTouch()` calls for specific interactions instead.
|
|
1100
|
+
|
|
1101
|
+
### High Memory Usage
|
|
1102
|
+
|
|
1103
|
+
**Symptom**: The app consumes more memory than expected after integrating the SDK.
|
|
1104
|
+
|
|
1105
|
+
**Possible causes and fixes**:
|
|
1106
|
+
|
|
1107
|
+
- **Snapshots enabled**: Snapshot capture serializes the component tree at regular intervals. This can be memory-intensive. Disable snapshots (`enableSnapshots: false`) if you do not need session replay.
|
|
1108
|
+
- **Large `batchSize`**: A very large batch size means more events are held in memory before being sent. Reduce `batchSize` to flush more frequently.
|
|
1109
|
+
- **Long sessions**: The `maxSnapshotsPerSession` limit (default 3600) prevents unbounded snapshot growth, but screen visits and events still accumulate. Consider calling `flush()` periodically for very long sessions.
|
|
1110
|
+
|
|
1111
|
+
### Crashes Not Being Reported
|
|
1112
|
+
|
|
1113
|
+
**Symptom**: `enableCrashTracking` is `true`, but no crash reports appear in the dashboard.
|
|
1114
|
+
|
|
1115
|
+
**Possible causes and fixes**:
|
|
1116
|
+
|
|
1117
|
+
- **Session not started**: Crash reports include the session ID for context, but they are sent even without a session. Check that the SDK is initialized (not just the session).
|
|
1118
|
+
- **Network failure on crash**: If the app crashes before the crash report HTTP request completes, the report may be lost. This is inherent to crash reporting in JavaScript -- native crash reporting libraries handle this better.
|
|
1119
|
+
- **Error caught elsewhere**: If another error handler (e.g., Sentry, Bugsnag) catches the error first and does not rethrow it, Repliqo may not see it. The SDK preserves the original error handler chain, so this should not normally be an issue.
|
|
1120
|
+
|
|
1121
|
+
---
|
|
1122
|
+
|
|
1123
|
+
## 17. TypeScript Types
|
|
1124
|
+
|
|
1125
|
+
All types are exported from the main package entry point. You can import them with:
|
|
1126
|
+
|
|
1127
|
+
```typescript
|
|
1128
|
+
import type {
|
|
1129
|
+
EventType,
|
|
1130
|
+
AnalyticsEvent,
|
|
1131
|
+
TouchEventData,
|
|
1132
|
+
NavigationEventData,
|
|
1133
|
+
ScreenVisit,
|
|
1134
|
+
DeviceInfo,
|
|
1135
|
+
SDKConfig,
|
|
1136
|
+
ComponentNode,
|
|
1137
|
+
SnapshotData,
|
|
1138
|
+
SnapshotDiff,
|
|
1139
|
+
DiffPatch,
|
|
1140
|
+
SnapshotPayload,
|
|
1141
|
+
CrashReport,
|
|
1142
|
+
ResolvedConfig,
|
|
1143
|
+
SnapshotCaptureConfig,
|
|
1144
|
+
} from '@repliqo/sdk-react-native';
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
### `EventType`
|
|
1148
|
+
|
|
1149
|
+
```typescript
|
|
1150
|
+
type EventType = 'touch' | 'scroll' | 'gesture' | 'navigation' | 'custom';
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
### `AnalyticsEvent`
|
|
1154
|
+
|
|
1155
|
+
```typescript
|
|
1156
|
+
interface AnalyticsEvent {
|
|
1157
|
+
type: EventType;
|
|
1158
|
+
screenName?: string;
|
|
1159
|
+
data: Record<string, any>;
|
|
1160
|
+
timestamp: string; // ISO 8601
|
|
1161
|
+
}
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
### `TouchEventData`
|
|
1165
|
+
|
|
1166
|
+
```typescript
|
|
1167
|
+
interface TouchEventData {
|
|
1168
|
+
x: number;
|
|
1169
|
+
y: number;
|
|
1170
|
+
elementTag?: string;
|
|
1171
|
+
elementText?: string;
|
|
1172
|
+
}
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
### `NavigationEventData`
|
|
1176
|
+
|
|
1177
|
+
```typescript
|
|
1178
|
+
interface NavigationEventData {
|
|
1179
|
+
fromScreen: string;
|
|
1180
|
+
toScreen: string;
|
|
1181
|
+
}
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
### `ScreenVisit`
|
|
1185
|
+
|
|
1186
|
+
```typescript
|
|
1187
|
+
interface ScreenVisit {
|
|
1188
|
+
sessionId: string;
|
|
1189
|
+
screenName: string;
|
|
1190
|
+
enteredAt: string; // ISO 8601
|
|
1191
|
+
exitedAt?: string; // ISO 8601
|
|
1192
|
+
duration?: number; // Milliseconds
|
|
1193
|
+
}
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
### `DeviceInfo`
|
|
1197
|
+
|
|
1198
|
+
```typescript
|
|
1199
|
+
interface DeviceInfo {
|
|
1200
|
+
os: string;
|
|
1201
|
+
osVersion: string;
|
|
1202
|
+
model: string;
|
|
1203
|
+
brand: string;
|
|
1204
|
+
screenWidth: number;
|
|
1205
|
+
screenHeight: number;
|
|
1206
|
+
appVersion?: string;
|
|
1207
|
+
}
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
### `SDKConfig`
|
|
1211
|
+
|
|
1212
|
+
```typescript
|
|
1213
|
+
interface SDKConfig {
|
|
1214
|
+
apiKey: string;
|
|
1215
|
+
appId: string;
|
|
1216
|
+
baseUrl: string;
|
|
1217
|
+
batchSize?: number;
|
|
1218
|
+
flushInterval?: number;
|
|
1219
|
+
enableTouchTracking?: boolean;
|
|
1220
|
+
enableNavigationTracking?: boolean;
|
|
1221
|
+
enableSnapshots?: boolean;
|
|
1222
|
+
snapshotInterval?: number;
|
|
1223
|
+
maxSnapshotsPerSession?: number;
|
|
1224
|
+
enableCrashTracking?: boolean;
|
|
1225
|
+
debug?: boolean;
|
|
1226
|
+
}
|
|
1227
|
+
```
|
|
1228
|
+
|
|
1229
|
+
### `CrashReport`
|
|
1230
|
+
|
|
1231
|
+
```typescript
|
|
1232
|
+
interface CrashReport {
|
|
1233
|
+
sessionId?: string;
|
|
1234
|
+
type: 'js_error' | 'native_crash' | 'unhandled_rejection';
|
|
1235
|
+
message: string;
|
|
1236
|
+
stackTrace?: string;
|
|
1237
|
+
screenName?: string;
|
|
1238
|
+
deviceInfo?: Record<string, any>;
|
|
1239
|
+
metadata?: Record<string, any>;
|
|
1240
|
+
timestamp: string; // ISO 8601
|
|
1241
|
+
}
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
### `ComponentNode`
|
|
1245
|
+
|
|
1246
|
+
```typescript
|
|
1247
|
+
interface ComponentNode {
|
|
1248
|
+
type: string;
|
|
1249
|
+
props: Record<string, any>;
|
|
1250
|
+
text?: string;
|
|
1251
|
+
children?: ComponentNode[];
|
|
1252
|
+
bounds?: {
|
|
1253
|
+
x: number;
|
|
1254
|
+
y: number;
|
|
1255
|
+
width: number;
|
|
1256
|
+
height: number;
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
### `SnapshotData`
|
|
1262
|
+
|
|
1263
|
+
```typescript
|
|
1264
|
+
interface SnapshotData {
|
|
1265
|
+
screenName: string;
|
|
1266
|
+
timestamp: string;
|
|
1267
|
+
sequence: number;
|
|
1268
|
+
isDiff: boolean;
|
|
1269
|
+
tree: ComponentNode | SnapshotDiff;
|
|
1270
|
+
screenDimensions: {
|
|
1271
|
+
width: number;
|
|
1272
|
+
height: number;
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
### `SnapshotDiff`
|
|
1278
|
+
|
|
1279
|
+
```typescript
|
|
1280
|
+
interface SnapshotDiff {
|
|
1281
|
+
type: 'diff';
|
|
1282
|
+
patches: DiffPatch[];
|
|
1283
|
+
}
|
|
1284
|
+
```
|
|
1285
|
+
|
|
1286
|
+
### `DiffPatch`
|
|
1287
|
+
|
|
1288
|
+
```typescript
|
|
1289
|
+
interface DiffPatch {
|
|
1290
|
+
path: string;
|
|
1291
|
+
op: 'add' | 'remove' | 'replace';
|
|
1292
|
+
value?: any;
|
|
1293
|
+
oldValue?: any;
|
|
1294
|
+
}
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
### `SnapshotPayload`
|
|
1298
|
+
|
|
1299
|
+
```typescript
|
|
1300
|
+
interface SnapshotPayload {
|
|
1301
|
+
sessionId: string;
|
|
1302
|
+
screenName: string;
|
|
1303
|
+
timestamp: string;
|
|
1304
|
+
sequence: number;
|
|
1305
|
+
isDiff: boolean;
|
|
1306
|
+
data: Record<string, any>;
|
|
1307
|
+
}
|
|
1308
|
+
```
|
|
1309
|
+
|
|
1310
|
+
---
|
|
1311
|
+
|
|
1312
|
+
**Need help?** Open an issue on the Repliqo repository or reach out through the dashboard at [https://repliqo.odmservice.site](https://repliqo.odmservice.site).
|