@teardown/react-native 2.0.4 → 2.0.10

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.
@@ -1,301 +0,0 @@
1
- # Identity & Authentication
2
-
3
- Manage user sessions and device identity with the IdentityClient.
4
-
5
- ## Overview
6
-
7
- The IdentityClient handles:
8
- - Device fingerprinting
9
- - User identification and session management
10
- - Anonymous and identified user states
11
- - Automatic session persistence
12
-
13
- ## Identity States
14
-
15
- The SDK uses a discriminated union to represent identity states:
16
-
17
- ```typescript
18
- type IdentifyState =
19
- | { type: "unidentified" }
20
- | { type: "identifying" }
21
- | { type: "identified", session: Session, version_info: {...} }
22
- ```
23
-
24
- ### State Transitions
25
-
26
- ```
27
- unidentified → identifying → identified
28
- ↑ ↓
29
- └───────── reset() ─────────┘
30
- ```
31
-
32
- ## Using the Identity Client
33
-
34
- ### Access via Hook
35
-
36
- ```typescript
37
- import { useSession } from '@teardown/react-native';
38
-
39
- function MyComponent() {
40
- const session = useSession();
41
-
42
- if (!session) {
43
- return <LoginScreen />;
44
- }
45
-
46
- return <div>Welcome, {session.persona_id}</div>;
47
- }
48
- ```
49
-
50
- ### Access via Core
51
-
52
- ```typescript
53
- import { useTeardown } from '@teardown/react-native';
54
-
55
- function MyComponent() {
56
- const { core } = useTeardown();
57
-
58
- const handleLogin = async () => {
59
- const result = await core.identity.identify({
60
- user_id: 'user-123',
61
- email: 'user@example.com',
62
- name: 'John Doe',
63
- });
64
- };
65
- }
66
- ```
67
-
68
- ## Identifying Users
69
-
70
- ### Anonymous Identification
71
-
72
- Automatically happens on SDK initialization:
73
-
74
- ```typescript
75
- // Happens automatically when TeardownProvider mounts
76
- await core.identity.identify();
77
- ```
78
-
79
- This creates an anonymous session with:
80
- - Unique device ID
81
- - Session ID
82
- - Device information
83
- - Version status
84
-
85
- ### Identified Users
86
-
87
- Identify users with their information:
88
-
89
- ```typescript
90
- const result = await core.identity.identify({
91
- user_id: 'user-123',
92
- email: 'user@example.com',
93
- name: 'John Doe',
94
- });
95
-
96
- if (result.success) {
97
- console.log('Session:', result.data.session_id);
98
- console.log('Device:', result.data.device_id);
99
- console.log('Persona:', result.data.persona_id);
100
- console.log('Token:', result.data.token);
101
- }
102
- ```
103
-
104
- ### Persona Object
105
-
106
- ```typescript
107
- type Persona = {
108
- user_id?: string;
109
- email?: string;
110
- name?: string;
111
- }
112
- ```
113
-
114
- All fields are optional. You can identify with just an email, just a user_id, or any combination.
115
-
116
- ## Session Management
117
-
118
- ### Getting Current Session
119
-
120
- ```typescript
121
- // Via hook (reactive)
122
- const session = useSession();
123
-
124
- // Via client (direct)
125
- const session = core.identity.getSessionState();
126
- ```
127
-
128
- ### Session Object
129
-
130
- ```typescript
131
- type Session = {
132
- session_id: string; // Unique session identifier
133
- device_id: string; // Unique device identifier
134
- persona_id: string; // User/persona identifier
135
- token: string; // Authentication token
136
- }
137
- ```
138
-
139
- ### Refreshing Session
140
-
141
- Re-identify the current persona to refresh session data:
142
-
143
- ```typescript
144
- const result = await core.identity.refresh();
145
-
146
- if (result.success) {
147
- console.log('Session refreshed');
148
- }
149
- ```
150
-
151
- Note: `refresh()` only works if already identified.
152
-
153
- ## Logging Out
154
-
155
- Reset the identity state to anonymous:
156
-
157
- ```typescript
158
- core.identity.reset();
159
- ```
160
-
161
- This will:
162
- - Clear persisted session data
163
- - Set state to "unidentified"
164
- - Emit state change event
165
- - Trigger re-identification on next app open
166
-
167
- ## Listening to State Changes
168
-
169
- ### Via Hook
170
-
171
- ```typescript
172
- const session = useSession();
173
-
174
- // Automatically re-renders on state changes
175
- useEffect(() => {
176
- console.log('Session changed:', session);
177
- }, [session]);
178
- ```
179
-
180
- ### Via Event Listener
181
-
182
- ```typescript
183
- useEffect(() => {
184
- const unsubscribe = core.identity.onIdentifyStateChange((state) => {
185
- console.log('State changed:', state.type);
186
-
187
- if (state.type === 'identified') {
188
- console.log('User session:', state.session);
189
- }
190
- });
191
-
192
- return unsubscribe;
193
- }, []);
194
- ```
195
-
196
- ## Identity State
197
-
198
- Get the full identity state (not just session):
199
-
200
- ```typescript
201
- const state = core.identity.getIdentifyState();
202
-
203
- switch (state.type) {
204
- case 'unidentified':
205
- // No session yet
206
- break;
207
- case 'identifying':
208
- // Identifying in progress
209
- break;
210
- case 'identified':
211
- // Has session and version info
212
- console.log(state.session);
213
- console.log(state.version_info);
214
- break;
215
- }
216
- ```
217
-
218
- ## Error Handling
219
-
220
- All identity operations return `AsyncResult`:
221
-
222
- ```typescript
223
- const result = await core.identity.identify({
224
- email: 'invalid-email',
225
- });
226
-
227
- if (!result.success) {
228
- console.error('Identification failed:', result.error);
229
- // Handle error (show message, retry, etc.)
230
- }
231
- ```
232
-
233
- Common errors:
234
- - Network errors
235
- - Invalid API credentials
236
- - Validation errors (422)
237
- - Server errors
238
-
239
- ## Best Practices
240
-
241
- ### 1. Let Auto-Initialization Happen
242
-
243
- ```typescript
244
- // ✅ Good - automatic anonymous identification
245
- <TeardownProvider core={teardown}>
246
- <App />
247
- </TeardownProvider>
248
-
249
- // ❌ Bad - don't manually call identify on mount
250
- useEffect(() => {
251
- core.identity.identify();
252
- }, []);
253
- ```
254
-
255
- ### 2. Identify on Login/Signup
256
-
257
- ```typescript
258
- const handleLogin = async (email: string, password: string) => {
259
- // Your auth logic
260
- const user = await login(email, password);
261
-
262
- // Identify with Teardown
263
- await core.identity.identify({
264
- user_id: user.id,
265
- email: user.email,
266
- name: user.name,
267
- });
268
- };
269
- ```
270
-
271
- ### 3. Reset on Logout
272
-
273
- ```typescript
274
- const handleLogout = async () => {
275
- // Your logout logic
276
- await logout();
277
-
278
- // Reset Teardown identity
279
- core.identity.reset();
280
- };
281
- ```
282
-
283
- ### 4. Use the Hook for Reactive UI
284
-
285
- ```typescript
286
- function UserProfile() {
287
- const session = useSession();
288
-
289
- if (!session) {
290
- return <LoginPrompt />;
291
- }
292
-
293
- return <Profile personaId={session.persona_id} />;
294
- }
295
- ```
296
-
297
- ## Next Steps
298
-
299
- - [Force Updates](./04-force-updates.mdx)
300
- - [Device Information](./05-device-info.mdx)
301
- - [API Reference](./07-api-reference.mdx)
@@ -1,339 +0,0 @@
1
- # Force Updates
2
-
3
- Manage app version requirements and force updates with the ForceUpdateClient.
4
-
5
- ## Overview
6
-
7
- The ForceUpdateClient automatically:
8
- - Checks app version against your backend
9
- - Monitors app state changes (background/foreground)
10
- - Throttles version checks to prevent excessive API calls
11
- - Emits version status changes for UI updates
12
-
13
- ## Version Status Types
14
-
15
- ```typescript
16
- type VersionStatus =
17
- | { type: "initializing" } // Initial state
18
- | { type: "checking" } // Currently checking version
19
- | { type: "up_to_date" } // Version is current
20
- | { type: "update_available" } // New version available (optional)
21
- | { type: "update_recommended" } // Update is recommended
22
- | { type: "update_required" } // Update is required
23
- | { type: "disabled" } // Version/build is disabled
24
- ```
25
-
26
- ## Using Force Updates
27
-
28
- ### Access via Hook
29
-
30
- ```typescript
31
- import { useForceUpdate } from '@teardown/react-native';
32
-
33
- function MyComponent() {
34
- const { versionStatus, isUpdateRequired, isUpdateAvailable } = useForceUpdate();
35
-
36
- if (isUpdateRequired) {
37
- return <UpdateRequiredScreen />;
38
- }
39
-
40
- if (isUpdateAvailable) {
41
- return <UpdateAvailableBanner />;
42
- }
43
-
44
- return <App />;
45
- }
46
- ```
47
-
48
- ### Hook Return Value
49
-
50
- ```typescript
51
- type UseForceUpdateResult = {
52
- versionStatus: VersionStatus;
53
- isUpdateAvailable: boolean; // true if any update exists
54
- isUpdateRequired: boolean; // true if update is required
55
- }
56
- ```
57
-
58
- ## Configuration
59
-
60
- Configure force update behavior when initializing TeardownCore:
61
-
62
- ```typescript
63
- const teardown = new TeardownCore({
64
- // ... other options
65
- forceUpdate: {
66
- // Minimum time between foreground checks (default: 30000ms = 30s)
67
- throttleMs: 30_000,
68
-
69
- // Minimum time since last successful check (default: 300000ms = 5min)
70
- checkCooldownMs: 300_000,
71
- },
72
- });
73
- ```
74
-
75
- ### Configuration Options
76
-
77
- | Option | Default | Description |
78
- |--------|---------|-------------|
79
- | `throttleMs` | 30000 | Min milliseconds between foreground transitions before checking |
80
- | `checkCooldownMs` | 300000 | Min milliseconds since last successful check before re-checking |
81
-
82
- ## How It Works
83
-
84
- ### Automatic Checks
85
-
86
- The SDK automatically checks version in these scenarios:
87
-
88
- 1. **Initial Identification**
89
- - When the SDK initializes
90
- - When `identify()` is called
91
-
92
- 2. **App Foreground**
93
- - When app returns from background
94
- - Subject to throttle and cooldown timers
95
-
96
- ### Check Flow
97
-
98
- ```
99
- App goes to foreground
100
-
101
- Check throttle timer (last foreground < throttleMs ago?)
102
- ↓ No
103
- Check cooldown timer (last check < checkCooldownMs ago?)
104
- ↓ No
105
- Call identify() to refresh version status
106
-
107
- Update version status
108
-
109
- Emit VERSION_STATUS_CHANGED event
110
- ```
111
-
112
- ### Throttle vs Cooldown
113
-
114
- - **Throttle**: Prevents checks when rapidly switching apps
115
- - **Cooldown**: Prevents excessive API calls even with long background periods
116
-
117
- Example:
118
- ```typescript
119
- // User switches between apps rapidly:
120
- // 10:00:00 - Foreground (check)
121
- // 10:00:05 - Foreground (throttled - too soon)
122
- // 10:00:45 - Foreground (check - passed throttle)
123
- // 10:00:50 - Foreground (cooldown - last check < 5min ago)
124
- ```
125
-
126
- ## Building Update UI
127
-
128
- ### Required Update Screen
129
-
130
- ```typescript
131
- function UpdateRequiredScreen() {
132
- const { isUpdateRequired, versionStatus } = useForceUpdate();
133
-
134
- if (!isUpdateRequired) return null;
135
-
136
- return (
137
- <View style={styles.container}>
138
- <Text>Update Required</Text>
139
- <Text>Please update to continue using the app</Text>
140
- <Button
141
- title="Update Now"
142
- onPress={() => Linking.openURL('market://details?id=com.yourapp')}
143
- />
144
- </View>
145
- );
146
- }
147
- ```
148
-
149
- ### Optional Update Banner
150
-
151
- ```typescript
152
- function UpdateBanner() {
153
- const { isUpdateAvailable, isUpdateRequired } = useForceUpdate();
154
- const [dismissed, setDismissed] = useState(false);
155
-
156
- // Don't show if required (use dedicated screen)
157
- if (isUpdateRequired || !isUpdateAvailable || dismissed) {
158
- return null;
159
- }
160
-
161
- return (
162
- <View style={styles.banner}>
163
- <Text>A new version is available</Text>
164
- <Button title="Update" onPress={handleUpdate} />
165
- <Button title="Later" onPress={() => setDismissed(true)} />
166
- </View>
167
- );
168
- }
169
- ```
170
-
171
- ### Full-Screen Takeover
172
-
173
- Example from the SDK:
174
-
175
- ```typescript
176
- function FullscreenTakeover() {
177
- const { isUpdateRequired } = useForceUpdate();
178
-
179
- if (!isUpdateRequired) return null;
180
-
181
- return (
182
- <Modal visible={true} animationType="fade">
183
- <UpdateRequiredScreen />
184
- </Modal>
185
- );
186
- }
187
-
188
- // In your layout
189
- <TeardownProvider core={teardown}>
190
- <App />
191
- <FullscreenTakeover />
192
- </TeardownProvider>
193
- ```
194
-
195
- ## Listening to Status Changes
196
-
197
- ### Via Hook (Recommended)
198
-
199
- ```typescript
200
- const { versionStatus } = useForceUpdate();
201
-
202
- useEffect(() => {
203
- console.log('Version status:', versionStatus.type);
204
- }, [versionStatus]);
205
- ```
206
-
207
- ### Via Event Listener
208
-
209
- ```typescript
210
- import { useTeardown } from '@teardown/react-native';
211
-
212
- function MyComponent() {
213
- const { core } = useTeardown();
214
-
215
- useEffect(() => {
216
- const unsubscribe = core.forceUpdate.onVersionStatusChange((status) => {
217
- console.log('Status changed:', status.type);
218
-
219
- if (status.type === 'update_required') {
220
- // Show update UI
221
- }
222
- });
223
-
224
- return unsubscribe;
225
- }, []);
226
- }
227
- ```
228
-
229
- ## Getting Current Status
230
-
231
- ```typescript
232
- // Via hook (reactive)
233
- const { versionStatus } = useForceUpdate();
234
-
235
- // Via client (direct)
236
- const status = core.forceUpdate.getVersionStatus();
237
- ```
238
-
239
- ## Platform-Specific Store Links
240
-
241
- ### iOS
242
-
243
- ```typescript
244
- const APP_STORE_ID = 'your-app-id';
245
- const APP_STORE_URL = `https://apps.apple.com/app/id${APP_STORE_ID}`;
246
-
247
- <Button
248
- title="Update"
249
- onPress={() => Linking.openURL(APP_STORE_URL)}
250
- />
251
- ```
252
-
253
- ### Android
254
-
255
- ```typescript
256
- const PACKAGE_NAME = 'com.yourcompany.yourapp';
257
- const PLAY_STORE_URL = `market://details?id=${PACKAGE_NAME}`;
258
- const PLAY_STORE_WEB = `https://play.google.com/store/apps/details?id=${PACKAGE_NAME}`;
259
-
260
- const handleUpdate = async () => {
261
- const supported = await Linking.canOpenURL(PLAY_STORE_URL);
262
- const url = supported ? PLAY_STORE_URL : PLAY_STORE_WEB;
263
- Linking.openURL(url);
264
- };
265
- ```
266
-
267
- ### Cross-Platform
268
-
269
- ```typescript
270
- import { Platform, Linking } from 'react-native';
271
-
272
- const STORE_URL = Platform.select({
273
- ios: 'https://apps.apple.com/app/id123456789',
274
- android: 'market://details?id=com.yourapp',
275
- default: 'https://yourapp.com/download',
276
- });
277
-
278
- <Button
279
- title="Update Now"
280
- onPress={() => Linking.openURL(STORE_URL)}
281
- />
282
- ```
283
-
284
- ## Version Status in Backend
285
-
286
- Version status is determined by your backend configuration:
287
-
288
- - `UP_TO_DATE`: Current version matches or is newer than minimum
289
- - `UPDATE_AVAILABLE`: New version exists, but not required
290
- - `UPDATE_RECOMMENDED`: New version exists, recommended to update
291
- - `UPDATE_REQUIRED`: Current version is below minimum required
292
- - `DISABLED`: This specific version/build is disabled
293
-
294
- Configure these in your Teardown dashboard under project settings.
295
-
296
- ## Best Practices
297
-
298
- ### 1. Use Full-Screen Takeover for Required Updates
299
-
300
- ```typescript
301
- // ✅ Good - blocks app usage
302
- {isUpdateRequired && <UpdateRequiredModal />}
303
-
304
- // ❌ Bad - user can dismiss and continue with outdated version
305
- {isUpdateRequired && <UpdateBanner dismissible />}
306
- ```
307
-
308
- ### 2. Make Optional Updates Dismissible
309
-
310
- ```typescript
311
- // ✅ Good - user can choose when to update
312
- {isUpdateAvailable && !isUpdateRequired && <DismissibleBanner />}
313
- ```
314
-
315
- ### 3. Respect User Decisions
316
-
317
- ```typescript
318
- // ✅ Good - remember dismissal for session
319
- const [dismissed, setDismissed] = useState(false);
320
-
321
- // ❌ Bad - annoying the user every render
322
- {isUpdateAvailable && <Banner />}
323
- ```
324
-
325
- ### 4. Provide Clear CTAs
326
-
327
- ```typescript
328
- // ✅ Good - clear action
329
- <Button title="Update Now" onPress={openStore} />
330
-
331
- // ❌ Bad - vague
332
- <Button title="OK" onPress={openStore} />
333
- ```
334
-
335
- ## Next Steps
336
-
337
- - [Device Information](./05-device-info.mdx)
338
- - [Hooks Reference](./08-hooks-reference.mdx)
339
- - [Advanced Usage](./09-advanced.mdx)