@teardown/react-native 2.0.29 → 2.0.30
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/README.md +22 -19
- package/docs/adapters/device/basic.mdx +7 -7
- package/docs/adapters/device/device-info.mdx +8 -8
- package/docs/adapters/device/expo.mdx +10 -10
- package/docs/adapters/device/index.mdx +51 -20
- package/docs/adapters/notifications/expo.mdx +64 -30
- package/docs/adapters/notifications/firebase.mdx +61 -27
- package/docs/adapters/notifications/index.mdx +120 -39
- package/docs/adapters/notifications/wix.mdx +61 -27
- package/docs/advanced.mdx +99 -3
- package/docs/api-reference.mdx +177 -12
- package/docs/core-concepts.mdx +31 -13
- package/docs/force-updates.mdx +18 -8
- package/docs/hooks-reference.mdx +9 -6
- package/docs/identity.mdx +46 -6
- package/docs/logging.mdx +5 -4
- package/package.json +5 -5
- package/src/clients/api/api.client.ts +4 -0
- package/src/clients/device/device.client.ts +9 -0
- package/src/clients/events/events.client.ts +102 -0
- package/src/clients/events/index.ts +1 -0
- package/src/clients/force-update/force-update.client.ts +26 -9
- package/src/clients/identity/identity.client.test.ts +1 -0
- package/src/clients/identity/identity.client.ts +265 -41
- package/src/exports/index.ts +1 -0
- package/src/hooks/use-force-update.ts +14 -0
- package/src/teardown.core.ts +42 -3
package/docs/api-reference.mdx
CHANGED
|
@@ -21,8 +21,11 @@ new TeardownCore(options: TeardownCoreOptions)
|
|
|
21
21
|
| `org_id` | `string` | Yes | Organization ID from dashboard |
|
|
22
22
|
| `project_id` | `string` | Yes | Project ID from dashboard |
|
|
23
23
|
| `api_key` | `string` | Yes | API key from dashboard |
|
|
24
|
+
| `environment_slug` | `string` | No | Environment slug (default: "production") |
|
|
25
|
+
| `ingestUrl` | `string` | No | Custom ingest API URL for local/staging |
|
|
24
26
|
| `storageAdapter` | `StorageAdapter` | Yes | Storage adapter instance |
|
|
25
27
|
| `deviceAdapter` | `DeviceInfoAdapter` | Yes | Device info adapter instance |
|
|
28
|
+
| `notificationAdapter` | `NotificationAdapter` | No | Push notification adapter instance |
|
|
26
29
|
| `forceUpdate` | `ForceUpdateClientOptions` | No | Force update configuration |
|
|
27
30
|
|
|
28
31
|
### Methods
|
|
@@ -38,8 +41,10 @@ new TeardownCore(options: TeardownCoreOptions)
|
|
|
38
41
|
|----------|------|-------------|
|
|
39
42
|
| `api` | `ApiClient` | Backend API client |
|
|
40
43
|
| `device` | `DeviceClient` | Device information client |
|
|
44
|
+
| `events` | `EventsClient` | Event tracking client |
|
|
41
45
|
| `identity` | `IdentityClient` | Identity management client |
|
|
42
46
|
| `forceUpdate` | `ForceUpdateClient` | Version checking client |
|
|
47
|
+
| `notifications` | `NotificationsClient \| undefined` | Push notifications client (if adapter provided) |
|
|
43
48
|
|
|
44
49
|
---
|
|
45
50
|
|
|
@@ -53,7 +58,8 @@ Manages user and device identification.
|
|
|
53
58
|
|--------|---------|-------------|
|
|
54
59
|
| `identify(persona?)` | `AsyncResult<IdentityUser>` | Identify user/device |
|
|
55
60
|
| `refresh()` | `AsyncResult<IdentityUser>` | Re-identify current user |
|
|
56
|
-
| `
|
|
61
|
+
| `signOut(options?)` | `AsyncResult<void>` | Sign out user, preserve device |
|
|
62
|
+
| `signOutAll(options?)` | `AsyncResult<void>` | Sign out and reset device |
|
|
57
63
|
| `getIdentifyState()` | `IdentifyState` | Get current state |
|
|
58
64
|
| `getSessionState()` | `Session \| null` | Get current session |
|
|
59
65
|
| `onIdentifyStateChange(listener)` | `() => void` | Subscribe to state changes |
|
|
@@ -89,6 +95,17 @@ type IdentifyState =
|
|
|
89
95
|
| { type: 'identified'; session: Session; version_info: VersionInfo };
|
|
90
96
|
```
|
|
91
97
|
|
|
98
|
+
### SignOutOptions
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
interface SignOutOptions {
|
|
102
|
+
// Additional properties to include in the sign out event
|
|
103
|
+
properties?: Record<string, unknown>;
|
|
104
|
+
// Wait for event to be sent before clearing state (default: true)
|
|
105
|
+
waitForEvent?: boolean;
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
92
109
|
---
|
|
93
110
|
|
|
94
111
|
## ForceUpdateClient
|
|
@@ -118,14 +135,144 @@ type VersionStatus =
|
|
|
118
135
|
| { type: 'initializing' }
|
|
119
136
|
| { type: 'checking' }
|
|
120
137
|
| { type: 'up_to_date' }
|
|
121
|
-
| { type: 'update_available' }
|
|
122
|
-
| { type: 'update_recommended' }
|
|
123
|
-
| { type: 'update_required' }
|
|
138
|
+
| { type: 'update_available'; releaseNotes?: string | null }
|
|
139
|
+
| { type: 'update_recommended'; releaseNotes?: string | null }
|
|
140
|
+
| { type: 'update_required'; releaseNotes?: string | null }
|
|
124
141
|
| { type: 'disabled' };
|
|
125
142
|
```
|
|
126
143
|
|
|
127
144
|
---
|
|
128
145
|
|
|
146
|
+
## EventsClient
|
|
147
|
+
|
|
148
|
+
Tracks events to the backend.
|
|
149
|
+
|
|
150
|
+
### Methods
|
|
151
|
+
|
|
152
|
+
| Method | Returns | Description |
|
|
153
|
+
|--------|---------|-------------|
|
|
154
|
+
| `track(event, sessionId?)` | `AsyncResult<void>` | Track a single event |
|
|
155
|
+
| `trackBatch(events, sessionId?)` | `AsyncResult<void>` | Track multiple events |
|
|
156
|
+
|
|
157
|
+
### EventPayload
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
interface EventPayload {
|
|
161
|
+
/** Name of the event (e.g., "button_clicked", "page_viewed") */
|
|
162
|
+
event_name: string;
|
|
163
|
+
/** Type of event */
|
|
164
|
+
event_type?: "action" | "screen_view" | "custom";
|
|
165
|
+
/** Additional properties to attach to the event */
|
|
166
|
+
properties?: Record<string, unknown>;
|
|
167
|
+
/** ISO timestamp. Defaults to current time if not provided */
|
|
168
|
+
timestamp?: string;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Usage
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
const { core } = useTeardown();
|
|
176
|
+
|
|
177
|
+
// Track a single event
|
|
178
|
+
await core.events.track({
|
|
179
|
+
event_name: 'button_clicked',
|
|
180
|
+
event_type: 'action',
|
|
181
|
+
properties: { button_id: 'submit-form' }
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Track multiple events
|
|
185
|
+
await core.events.trackBatch([
|
|
186
|
+
{ event_name: 'page_viewed', event_type: 'screen_view', properties: { page: 'home' } },
|
|
187
|
+
{ event_name: 'cta_shown', event_type: 'action' }
|
|
188
|
+
]);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## NotificationsClient
|
|
194
|
+
|
|
195
|
+
Manages push notifications across different providers. Only available if `notificationAdapter` is provided in TeardownCore options.
|
|
196
|
+
|
|
197
|
+
### Methods
|
|
198
|
+
|
|
199
|
+
| Method | Returns | Description |
|
|
200
|
+
|--------|---------|-------------|
|
|
201
|
+
| `requestPermissions()` | `Promise<PermissionStatus>` | Request notification permissions |
|
|
202
|
+
| `getToken()` | `Promise<string \| null>` | Get push notification token |
|
|
203
|
+
| `onTokenChange(listener)` | `Unsubscribe` | Subscribe to token changes |
|
|
204
|
+
| `onNotificationReceived(listener)` | `Unsubscribe` | Subscribe to foreground notifications |
|
|
205
|
+
| `onNotificationOpened(listener)` | `Unsubscribe` | Subscribe to notification taps |
|
|
206
|
+
| `onDataMessage(listener)` | `Unsubscribe` | Subscribe to silent/data messages |
|
|
207
|
+
| `destroy()` | `void` | Cleanup resources |
|
|
208
|
+
|
|
209
|
+
### Properties
|
|
210
|
+
|
|
211
|
+
| Property | Type | Description |
|
|
212
|
+
|----------|------|-------------|
|
|
213
|
+
| `platform` | `NotificationPlatformEnum` | Notification platform (APNS, FCM, EXPO) |
|
|
214
|
+
|
|
215
|
+
### PermissionStatus
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
interface PermissionStatus {
|
|
219
|
+
granted: boolean;
|
|
220
|
+
canAskAgain: boolean;
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### PushNotification
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
interface PushNotification {
|
|
228
|
+
title?: string;
|
|
229
|
+
body?: string;
|
|
230
|
+
data?: Record<string, unknown>;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### DataMessage
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
interface DataMessage {
|
|
238
|
+
data: Record<string, unknown>;
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### NotificationPlatformEnum
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
enum NotificationPlatformEnum {
|
|
246
|
+
APNS = "APNS", // Apple Push Notification Service
|
|
247
|
+
FCM = "FCM", // Firebase Cloud Messaging
|
|
248
|
+
EXPO = "EXPO", // Expo Push Notifications
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Usage
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
const { core } = useTeardown();
|
|
256
|
+
|
|
257
|
+
if (core.notifications) {
|
|
258
|
+
// Request permissions
|
|
259
|
+
const status = await core.notifications.requestPermissions();
|
|
260
|
+
|
|
261
|
+
// Get token for backend
|
|
262
|
+
const token = await core.notifications.getToken();
|
|
263
|
+
|
|
264
|
+
// Listen for foreground notifications
|
|
265
|
+
const unsub = core.notifications.onNotificationReceived((notification) => {
|
|
266
|
+
console.log('Notification:', notification.title);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Cleanup
|
|
270
|
+
unsub();
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
129
276
|
## DeviceClient
|
|
130
277
|
|
|
131
278
|
Collects device information.
|
|
@@ -136,15 +283,18 @@ Collects device information.
|
|
|
136
283
|
|--------|---------|-------------|
|
|
137
284
|
| `getDeviceId()` | `Promise<string>` | Get stable device UUID |
|
|
138
285
|
| `getDeviceInfo()` | `Promise<DeviceInfo>` | Get full device info |
|
|
286
|
+
| `reset()` | `void` | Clear stored deviceId (new ID on next call) |
|
|
139
287
|
|
|
140
288
|
### DeviceInfo
|
|
141
289
|
|
|
142
290
|
```typescript
|
|
143
291
|
interface DeviceInfo {
|
|
144
|
-
timestamp
|
|
292
|
+
timestamp?: Date;
|
|
145
293
|
os: OSInfo;
|
|
146
294
|
hardware: HardwareInfo;
|
|
147
295
|
application: ApplicationInfo;
|
|
296
|
+
update: DeviceUpdateInfo | null;
|
|
297
|
+
notifications?: NotificationsInfo;
|
|
148
298
|
}
|
|
149
299
|
```
|
|
150
300
|
|
|
@@ -192,8 +342,7 @@ Interface for device information adapters.
|
|
|
192
342
|
```typescript
|
|
193
343
|
interface ApplicationInfo {
|
|
194
344
|
version: string;
|
|
195
|
-
|
|
196
|
-
bundleId: string;
|
|
345
|
+
build_number: number;
|
|
197
346
|
}
|
|
198
347
|
```
|
|
199
348
|
|
|
@@ -201,9 +350,9 @@ interface ApplicationInfo {
|
|
|
201
350
|
|
|
202
351
|
```typescript
|
|
203
352
|
interface HardwareInfo {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
353
|
+
device_name: string;
|
|
354
|
+
device_brand: string;
|
|
355
|
+
device_type: string;
|
|
207
356
|
}
|
|
208
357
|
```
|
|
209
358
|
|
|
@@ -211,8 +360,24 @@ interface HardwareInfo {
|
|
|
211
360
|
|
|
212
361
|
```typescript
|
|
213
362
|
interface OSInfo {
|
|
214
|
-
|
|
215
|
-
|
|
363
|
+
platform: DevicePlatformEnum;
|
|
364
|
+
name: string;
|
|
365
|
+
version: string;
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### DevicePlatformEnum
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
enum DevicePlatformEnum {
|
|
373
|
+
IOS = "IOS",
|
|
374
|
+
ANDROID = "ANDROID",
|
|
375
|
+
WEB = "WEB",
|
|
376
|
+
WINDOWS = "WINDOWS",
|
|
377
|
+
MACOS = "MACOS",
|
|
378
|
+
LINUX = "LINUX",
|
|
379
|
+
UNKNOWN = "UNKNOWN",
|
|
380
|
+
// ... and more
|
|
216
381
|
}
|
|
217
382
|
```
|
|
218
383
|
|
package/docs/core-concepts.mdx
CHANGED
|
@@ -10,12 +10,14 @@ The Teardown SDK is built around a central `TeardownCore` class that orchestrate
|
|
|
10
10
|
|
|
11
11
|
```
|
|
12
12
|
TeardownCore
|
|
13
|
-
├── IdentityClient
|
|
14
|
-
├── ForceUpdateClient
|
|
15
|
-
├── DeviceClient
|
|
16
|
-
├──
|
|
17
|
-
├──
|
|
18
|
-
|
|
13
|
+
├── IdentityClient # User/device identification
|
|
14
|
+
├── ForceUpdateClient # Version checking
|
|
15
|
+
├── DeviceClient # Device information
|
|
16
|
+
├── EventsClient # Event tracking
|
|
17
|
+
├── NotificationsClient # Push notifications (optional)
|
|
18
|
+
├── StorageClient # Persistent storage
|
|
19
|
+
├── ApiClient # Backend communication
|
|
20
|
+
└── LoggingClient # Structured logging
|
|
19
21
|
```
|
|
20
22
|
|
|
21
23
|
## Initialization Flow
|
|
@@ -58,9 +60,9 @@ type VersionStatus =
|
|
|
58
60
|
| { type: 'initializing' }
|
|
59
61
|
| { type: 'checking' }
|
|
60
62
|
| { type: 'up_to_date' }
|
|
61
|
-
| { type: 'update_available' }
|
|
62
|
-
| { type: 'update_recommended' }
|
|
63
|
-
| { type: 'update_required' }
|
|
63
|
+
| { type: 'update_available'; releaseNotes?: string | null }
|
|
64
|
+
| { type: 'update_recommended'; releaseNotes?: string | null }
|
|
65
|
+
| { type: 'update_required'; releaseNotes?: string | null }
|
|
64
66
|
| { type: 'disabled' };
|
|
65
67
|
```
|
|
66
68
|
|
|
@@ -88,10 +90,26 @@ interface SupportedStorage {
|
|
|
88
90
|
Provide device and app information:
|
|
89
91
|
|
|
90
92
|
```typescript
|
|
91
|
-
|
|
92
|
-
applicationInfo: ApplicationInfo; // version,
|
|
93
|
-
hardwareInfo: HardwareInfo; //
|
|
94
|
-
osInfo: OSInfo; //
|
|
93
|
+
abstract class DeviceInfoAdapter {
|
|
94
|
+
applicationInfo: ApplicationInfo; // version, build_number
|
|
95
|
+
hardwareInfo: HardwareInfo; // device_name, device_brand, device_type
|
|
96
|
+
osInfo: OSInfo; // platform, name, version
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Notification Adapters
|
|
101
|
+
|
|
102
|
+
Handle push notification registration and events (optional):
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
abstract class NotificationAdapter {
|
|
106
|
+
platform: NotificationPlatformEnum;
|
|
107
|
+
getToken(): Promise<string | null>;
|
|
108
|
+
requestPermissions(): Promise<PermissionStatus>;
|
|
109
|
+
onTokenRefresh(listener): Unsubscribe;
|
|
110
|
+
onNotificationReceived(listener): Unsubscribe;
|
|
111
|
+
onNotificationOpened(listener): Unsubscribe;
|
|
112
|
+
onDataMessage(listener): Unsubscribe;
|
|
95
113
|
}
|
|
96
114
|
```
|
|
97
115
|
|
package/docs/force-updates.mdx
CHANGED
|
@@ -21,12 +21,14 @@ type VersionStatus =
|
|
|
21
21
|
| { type: 'initializing' } // SDK starting up
|
|
22
22
|
| { type: 'checking' } // Version check in progress
|
|
23
23
|
| { type: 'up_to_date' } // Current version is valid
|
|
24
|
-
| { type: 'update_available' } // Optional update exists
|
|
25
|
-
| { type: 'update_recommended' } // Recommended update exists
|
|
26
|
-
| { type: 'update_required' } // Must update to continue
|
|
24
|
+
| { type: 'update_available'; releaseNotes?: string | null } // Optional update exists
|
|
25
|
+
| { type: 'update_recommended'; releaseNotes?: string | null } // Recommended update exists
|
|
26
|
+
| { type: 'update_required'; releaseNotes?: string | null } // Must update to continue
|
|
27
27
|
| { type: 'disabled' } // Version/build has been disabled
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
Update status types include an optional `releaseNotes` field containing notes set in the dashboard when changing version/build status.
|
|
31
|
+
|
|
30
32
|
## Basic Usage
|
|
31
33
|
|
|
32
34
|
### Using the Hook
|
|
@@ -35,16 +37,16 @@ type VersionStatus =
|
|
|
35
37
|
import { useForceUpdate } from '@teardown/react-native';
|
|
36
38
|
|
|
37
39
|
function App() {
|
|
38
|
-
const { versionStatus, isUpdateRequired, isUpdateAvailable, isUpdateRecommended } = useForceUpdate();
|
|
40
|
+
const { versionStatus, isUpdateRequired, isUpdateAvailable, isUpdateRecommended, releaseNotes } = useForceUpdate();
|
|
39
41
|
|
|
40
42
|
if (isUpdateRequired) {
|
|
41
|
-
return <ForceUpdateModal />;
|
|
43
|
+
return <ForceUpdateModal releaseNotes={releaseNotes} />;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
if (isUpdateRecommended) {
|
|
45
47
|
return (
|
|
46
48
|
<>
|
|
47
|
-
<UpdateBanner onDismiss={() => {}} />
|
|
49
|
+
<UpdateBanner releaseNotes={releaseNotes} onDismiss={() => {}} />
|
|
48
50
|
<MainApp />
|
|
49
51
|
</>
|
|
50
52
|
);
|
|
@@ -62,6 +64,7 @@ interface UseForceUpdateResult {
|
|
|
62
64
|
isUpdateAvailable: boolean; // Any update exists (available, recommended, or required)
|
|
63
65
|
isUpdateRecommended: boolean; // Update is recommended but not required
|
|
64
66
|
isUpdateRequired: boolean; // Update is mandatory
|
|
67
|
+
releaseNotes: string | null; // Release notes for the update, if available
|
|
65
68
|
}
|
|
66
69
|
```
|
|
67
70
|
|
|
@@ -114,7 +117,7 @@ unsubscribe();
|
|
|
114
117
|
### Force Update Modal
|
|
115
118
|
|
|
116
119
|
```tsx
|
|
117
|
-
function ForceUpdateModal() {
|
|
120
|
+
function ForceUpdateModal({ releaseNotes }: { releaseNotes: string | null }) {
|
|
118
121
|
const handleUpdate = () => {
|
|
119
122
|
// Open app store
|
|
120
123
|
Linking.openURL('https://apps.apple.com/app/your-app');
|
|
@@ -125,6 +128,12 @@ function ForceUpdateModal() {
|
|
|
125
128
|
<View style={styles.container}>
|
|
126
129
|
<Text>Update Required</Text>
|
|
127
130
|
<Text>Please update to continue using the app.</Text>
|
|
131
|
+
{releaseNotes && (
|
|
132
|
+
<View style={styles.releaseNotes}>
|
|
133
|
+
<Text style={styles.releaseNotesTitle}>What's New:</Text>
|
|
134
|
+
<Text>{releaseNotes}</Text>
|
|
135
|
+
</View>
|
|
136
|
+
)}
|
|
128
137
|
<Button onPress={handleUpdate} title="Update Now" />
|
|
129
138
|
</View>
|
|
130
139
|
</Modal>
|
|
@@ -135,7 +144,7 @@ function ForceUpdateModal() {
|
|
|
135
144
|
### Soft Update Banner
|
|
136
145
|
|
|
137
146
|
```tsx
|
|
138
|
-
function UpdateBanner({ onDismiss }: { onDismiss: () => void }) {
|
|
147
|
+
function UpdateBanner({ releaseNotes, onDismiss }: { releaseNotes: string | null; onDismiss: () => void }) {
|
|
139
148
|
const { isUpdateRecommended } = useForceUpdate();
|
|
140
149
|
|
|
141
150
|
if (!isUpdateRecommended) return null;
|
|
@@ -143,6 +152,7 @@ function UpdateBanner({ onDismiss }: { onDismiss: () => void }) {
|
|
|
143
152
|
return (
|
|
144
153
|
<View style={styles.banner}>
|
|
145
154
|
<Text>A new version is available</Text>
|
|
155
|
+
{releaseNotes && <Text style={styles.notes}>{releaseNotes}</Text>}
|
|
146
156
|
<Button onPress={onDismiss} title="Later" />
|
|
147
157
|
<Button onPress={() => Linking.openURL('...')} title="Update" />
|
|
148
158
|
</View>
|
package/docs/hooks-reference.mdx
CHANGED
|
@@ -105,7 +105,7 @@ Returns `null` if not identified.
|
|
|
105
105
|
|
|
106
106
|
The hook subscribes to `identity.onIdentifyStateChange()` and re-renders when:
|
|
107
107
|
- User identifies (session available)
|
|
108
|
-
- User
|
|
108
|
+
- User signs out (session becomes null)
|
|
109
109
|
- Session refreshes (new session data)
|
|
110
110
|
|
|
111
111
|
---
|
|
@@ -122,17 +122,18 @@ function App() {
|
|
|
122
122
|
versionStatus,
|
|
123
123
|
isUpdateAvailable,
|
|
124
124
|
isUpdateRecommended,
|
|
125
|
-
isUpdateRequired
|
|
125
|
+
isUpdateRequired,
|
|
126
|
+
releaseNotes
|
|
126
127
|
} = useForceUpdate();
|
|
127
128
|
|
|
128
129
|
if (isUpdateRequired) {
|
|
129
|
-
return <ForceUpdateModal />;
|
|
130
|
+
return <ForceUpdateModal releaseNotes={releaseNotes} />;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
if (isUpdateRecommended) {
|
|
133
134
|
return (
|
|
134
135
|
<>
|
|
135
|
-
<UpdateBanner />
|
|
136
|
+
<UpdateBanner releaseNotes={releaseNotes} />
|
|
136
137
|
<MainApp />
|
|
137
138
|
</>
|
|
138
139
|
);
|
|
@@ -154,6 +155,8 @@ interface UseForceUpdateResult {
|
|
|
154
155
|
isUpdateRecommended: boolean;
|
|
155
156
|
/** Update is mandatory */
|
|
156
157
|
isUpdateRequired: boolean;
|
|
158
|
+
/** Release notes for the update, if available (set in dashboard when changing status) */
|
|
159
|
+
releaseNotes: string | null;
|
|
157
160
|
}
|
|
158
161
|
```
|
|
159
162
|
|
|
@@ -222,8 +225,8 @@ function App() {
|
|
|
222
225
|
function LogoutButton() {
|
|
223
226
|
const { core } = useTeardown();
|
|
224
227
|
|
|
225
|
-
const handleLogout = () => {
|
|
226
|
-
core.identity.
|
|
228
|
+
const handleLogout = async () => {
|
|
229
|
+
await core.identity.signOut();
|
|
227
230
|
// Session will become null, useSession will re-render
|
|
228
231
|
};
|
|
229
232
|
|
package/docs/identity.mdx
CHANGED
|
@@ -58,12 +58,51 @@ if (session) {
|
|
|
58
58
|
}
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
###
|
|
61
|
+
### Sign Out
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
Sign out the current user while preserving the device identity:
|
|
64
64
|
|
|
65
65
|
```typescript
|
|
66
|
-
core.identity.
|
|
66
|
+
const result = await core.identity.signOut();
|
|
67
|
+
|
|
68
|
+
if (result.success) {
|
|
69
|
+
console.log('Signed out successfully');
|
|
70
|
+
} else {
|
|
71
|
+
console.error('Sign out failed:', result.error);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This clears the session and user association but keeps the device ID. The same device will be recognized on the next `identify()` call.
|
|
76
|
+
|
|
77
|
+
### Sign Out All (Full Reset)
|
|
78
|
+
|
|
79
|
+
Sign out and reset all SDK state including the device ID:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const result = await core.identity.signOutAll();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This clears everything - the device will appear as a fresh install on the next `identify()` call.
|
|
86
|
+
|
|
87
|
+
### Sign Out Options
|
|
88
|
+
|
|
89
|
+
Both `signOut()` and `signOutAll()` accept optional configuration:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
interface SignOutOptions {
|
|
93
|
+
// Additional properties to include in the sign out event
|
|
94
|
+
properties?: Record<string, unknown>;
|
|
95
|
+
// Wait for event to be sent before clearing state (default: true)
|
|
96
|
+
waitForEvent?: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Example: Fire-and-forget sign out
|
|
100
|
+
await core.identity.signOut({ waitForEvent: false });
|
|
101
|
+
|
|
102
|
+
// Example: Include custom properties
|
|
103
|
+
await core.identity.signOut({
|
|
104
|
+
properties: { reason: 'user_requested' }
|
|
105
|
+
});
|
|
67
106
|
```
|
|
68
107
|
|
|
69
108
|
### Refresh Session
|
|
@@ -166,6 +205,7 @@ if (!result.success) {
|
|
|
166
205
|
## Best Practices
|
|
167
206
|
|
|
168
207
|
1. **Call identify on login** - Associate users with their devices
|
|
169
|
-
2. **Call
|
|
170
|
-
3. **
|
|
171
|
-
4. **
|
|
208
|
+
2. **Call signOut on logout** - Clear session data and notify the backend
|
|
209
|
+
3. **Use signOutAll for account switching** - When users switch accounts, use `signOutAll()` for a clean slate
|
|
210
|
+
4. **Handle offline** - The SDK caches session data for offline use
|
|
211
|
+
5. **Use useSession hook** - For reactive UI updates
|
package/docs/logging.mdx
CHANGED
|
@@ -135,10 +135,11 @@ class Logger {
|
|
|
135
135
|
1. **Use `verbose` in development** - See all SDK activity
|
|
136
136
|
2. **Use `error` in production** - Minimize noise, catch issues
|
|
137
137
|
3. **Filter by prefix** - Isolate SDK logs from app logs
|
|
138
|
-
4. **
|
|
138
|
+
4. **Set log level based on environment**:
|
|
139
139
|
|
|
140
140
|
```typescript
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
const teardown = new TeardownCore({ /* ... */ });
|
|
142
|
+
|
|
143
|
+
// Set based on environment
|
|
144
|
+
teardown.setLogLevel(__DEV__ ? 'verbose' : 'error');
|
|
144
145
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teardown/react-native",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"prepublishOnly": "bun run build"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@teardown/ingest-api": "2.0.
|
|
56
|
-
"@teardown/schemas": "2.0.
|
|
57
|
-
"@teardown/types": "2.0.
|
|
55
|
+
"@teardown/ingest-api": "2.0.30",
|
|
56
|
+
"@teardown/schemas": "2.0.30",
|
|
57
|
+
"@teardown/types": "2.0.30",
|
|
58
58
|
"eventemitter3": "^5.0.1",
|
|
59
59
|
"react-native-get-random-values": "^2.0.0",
|
|
60
60
|
"uuid": "^13.0.0",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"@biomejs/biome": "2.3.10",
|
|
65
65
|
"@elysiajs/eden": "1.4.5",
|
|
66
66
|
"@react-native-firebase/messaging": "*",
|
|
67
|
-
"@teardown/tsconfig": "2.0.
|
|
67
|
+
"@teardown/tsconfig": "2.0.30",
|
|
68
68
|
"@types/bun": "1.3.5",
|
|
69
69
|
"@types/react": "~19.1.0",
|
|
70
70
|
"@types/uuid": "^11.0.0",
|
|
@@ -67,4 +67,13 @@ export class DeviceClient {
|
|
|
67
67
|
async getDeviceInfo(): Promise<DeviceInfo> {
|
|
68
68
|
return this.options.adapter.getDeviceInfo();
|
|
69
69
|
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Reset the device client by clearing the stored deviceId.
|
|
73
|
+
* This will generate a new deviceId on next getDeviceId() call.
|
|
74
|
+
*/
|
|
75
|
+
reset(): void {
|
|
76
|
+
this.logger.debug("Resetting device - clearing deviceId");
|
|
77
|
+
this.storage.removeItem("deviceId");
|
|
78
|
+
}
|
|
70
79
|
}
|