@userflow-tech/react-native 0.4.0 → 0.4.2
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 +107 -144
- package/docs/anchoring.md +3 -5
- package/docs/deep-linking.md +0 -17
- package/docs/troubleshooting.md +15 -20
- package/lib/commonjs/version.js +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/commonjs/src/version.d.ts +1 -1
- package/lib/typescript/module/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/version.ts +1 -1
- package/vendor/core/dist/constants.d.ts +0 -4
- package/vendor/core/dist/constants.d.ts.map +1 -1
- package/vendor/core/dist/constants.js +0 -4
- package/vendor/core/dist/constants.js.map +1 -1
- package/vendor/core/package.json +1 -1
- package/vendor/protocol/package.json +1 -1
- package/docs/theme-mapping.md +0 -130
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# `@userflow-tech/react-native`
|
|
2
2
|
|
|
3
3
|
Official Userflow SDK for React Native and Expo. Deliver guided flows, tooltips,
|
|
4
|
-
and
|
|
5
|
-
release for every content change.
|
|
4
|
+
modals, and banners authored in the Userflow dashboard — without shipping a new
|
|
5
|
+
app release for every content change.
|
|
6
6
|
|
|
7
7
|
> Early development. APIs may change.
|
|
8
8
|
|
|
@@ -10,10 +10,9 @@ release for every content change.
|
|
|
10
10
|
|
|
11
11
|
Declared minimums: React `>=18.2.0`, React Native `>=0.76.0`. React Native
|
|
12
12
|
0.74 and 0.75 cannot install this SDK: `react-native-view-shot` 5.x, which
|
|
13
|
-
Builder element selection needs, requires React Native 0.76 or later.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
not supported.
|
|
13
|
+
Builder element selection needs, requires React Native 0.76 or later. The SDK
|
|
14
|
+
is tested on React Native 0.81 / React 19.1 and on Expo SDK 57 / React Native
|
|
15
|
+
0.86. Expo Go is not supported.
|
|
17
16
|
|
|
18
17
|
`@react-native-async-storage/async-storage` is the only native module this
|
|
19
18
|
package depends on, so npm installs it. `react-native-webview` and
|
|
@@ -44,13 +43,9 @@ the ranges below.
|
|
|
44
43
|
Optional peers are declared `optional: true` in `peerDependenciesMeta`, so
|
|
45
44
|
package managers do not warn if you skip them.
|
|
46
45
|
|
|
47
|
-
npm
|
|
48
|
-
`react-native-
|
|
49
|
-
|
|
50
|
-
missing-peer warnings among unrelated ones. That is why the install commands
|
|
51
|
-
list WebView and view-shot explicitly. A clean npm install prints no warning
|
|
52
|
-
about optional peers, so install `react-native-safe-area-context` and NetInfo
|
|
53
|
-
yourself when you want the behavior in the table. An app that already depends
|
|
46
|
+
A clean npm install prints no warning about optional peers, so install
|
|
47
|
+
`react-native-safe-area-context` and NetInfo yourself when you want the behavior
|
|
48
|
+
in the table. An app that already depends
|
|
54
49
|
on an older major (view-shot 4.0.3, NetInfo 9.x, safe-area 3.x, WebView 13.6.x
|
|
55
50
|
or 14.x) fails install with `ERESOLVE`.
|
|
56
51
|
|
|
@@ -65,19 +60,8 @@ Use an **Expo development build** (or bare React Native). In development,
|
|
|
65
60
|
|
|
66
61
|
## Install
|
|
67
62
|
|
|
68
|
-
The package is public on npm
|
|
69
|
-
|
|
70
|
-
Yarn 2 and later ignore `.npmrc`. Add this to `.yarnrc.yml` instead:
|
|
71
|
-
|
|
72
|
-
```yaml
|
|
73
|
-
npmScopes:
|
|
74
|
-
userflow-tech:
|
|
75
|
-
npmRegistryServer: 'https://registry.npmjs.org'
|
|
76
|
-
npmAuthToken: '${NPM_TOKEN}'
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Set `NPM_TOKEN` in the environment that installs dependencies: a local shell,
|
|
80
|
-
CI, and EAS (an EAS secret named `NPM_TOKEN`, available to the install step).
|
|
63
|
+
The package is public on npm. No token, `.npmrc`, or `.yarnrc.yml` setup is
|
|
64
|
+
needed with any package manager.
|
|
81
65
|
|
|
82
66
|
### Packages
|
|
83
67
|
|
|
@@ -130,28 +114,12 @@ npx expo install react-native-safe-area-context @react-native-community/netinfo
|
|
|
130
114
|
# and either expo-router or @react-navigation/native, depending on your router
|
|
131
115
|
```
|
|
132
116
|
|
|
133
|
-
For local tarball installs from this monorepo, pack with `pnpm pack:all` at the
|
|
134
|
-
repo root and install the single `@userflow-tech/react-native` tarball. Steps:
|
|
135
|
-
`docs/local-testing.md` in the monorepo.
|
|
136
|
-
|
|
137
117
|
### Install with an AI agent
|
|
138
118
|
|
|
139
|
-
Cursor, Claude Code, and other agents
|
|
140
|
-
|
|
119
|
+
An Agent Skill that installs this SDK in Cursor, Claude Code, and other agents
|
|
120
|
+
will be available soon. It will be `install-react-native-with-ai` in
|
|
141
121
|
[userflow/ai-skills-library](https://github.com/userflow/ai-skills-library).
|
|
142
|
-
Web apps use `install-with-ai` from that library
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
npx skills add userflow/ai-skills-library@install-react-native-with-ai
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Claude Code loads it from the plugin:
|
|
149
|
-
|
|
150
|
-
```text
|
|
151
|
-
/plugin marketplace add userflow/ai-skills-library
|
|
152
|
-
/plugin install userflow@ai-skills-library
|
|
153
|
-
/userflow:install-react-native-with-ai
|
|
154
|
-
```
|
|
122
|
+
Web apps will use `install-with-ai` from that library.
|
|
155
123
|
|
|
156
124
|
## Quick start
|
|
157
125
|
|
|
@@ -159,9 +127,7 @@ Claude Code loads it from the plugin:
|
|
|
159
127
|
import { Userflow, UserflowProvider } from '@userflow-tech/react-native';
|
|
160
128
|
|
|
161
129
|
await Userflow.init({
|
|
162
|
-
clientToken: 'ct_...', // from the Userflow dashboard
|
|
163
|
-
// serverEndpoint: 'https://e.userflow.com', // default (US). EU: 'https://e.eu.userflow.com'
|
|
164
|
-
// pairingEndpoint: 'https://e.userflow.com', // default; pairing stays on the US host
|
|
130
|
+
clientToken: 'ct_...', // from Settings → Environments in the Userflow dashboard. Never an esk_ secret key.
|
|
165
131
|
});
|
|
166
132
|
|
|
167
133
|
// Identified user — signature is computed by YOUR backend, never in the app.
|
|
@@ -178,23 +144,33 @@ NetInfo subscriptions). Call `Userflow.reset()` on logout to clear identity and
|
|
|
178
144
|
disconnect. `reset()` does not apply a new `clientToken` or endpoint; restart
|
|
179
145
|
the app to change those.
|
|
180
146
|
|
|
147
|
+
### `init` options
|
|
148
|
+
|
|
149
|
+
| Option | Default | Description |
|
|
150
|
+
| ------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------ |
|
|
151
|
+
| `clientToken` | — | **Required.** The `ct_…` client token from Settings → Environments. |
|
|
152
|
+
| `onNavigate` | — | Handler for flow NAVIGATE actions. Wins over React Navigation, Expo Router, and Linking. See NAVIGATE below. |
|
|
153
|
+
| `appVersion` | Read from the native app | Override the host app version sent as `app_version`. |
|
|
154
|
+
| `appBuild` | Read from the native app | Build number sent as `app_build`, paired with `appVersion`. |
|
|
155
|
+
|
|
181
156
|
### Where to put `init`, `identify`, and `UserflowProvider`
|
|
182
157
|
|
|
183
158
|
Call `Userflow.init` once, as early as you can, from the root component
|
|
184
159
|
(`app/_layout.tsx` with Expo Router, `App.tsx` with bare React Native or React
|
|
185
160
|
Navigation). `UserflowProvider` can mount before that promise resolves. Put it
|
|
186
|
-
once, near the top of the tree, inside `SafeAreaProvider`.
|
|
161
|
+
once, near the top of the tree, inside `SafeAreaProvider`. Expo Router already
|
|
162
|
+
renders `SafeAreaProvider` above the root layout. React Navigation and bare
|
|
163
|
+
apps wrap the tree in `SafeAreaProvider` themselves. Call `identify` after
|
|
187
164
|
sign-in. `identifyAnonymous` identifies signed-out users and counts them toward
|
|
188
165
|
MAU, so use it only when you mean to. Call `Userflow.reset()` on logout.
|
|
189
166
|
|
|
190
|
-
Expo Router:
|
|
167
|
+
Expo Router already renders `SafeAreaProvider` above the root layout:
|
|
191
168
|
|
|
192
169
|
```tsx
|
|
193
170
|
import { useEffect } from 'react';
|
|
194
171
|
import { Userflow, UserflowProvider } from '@userflow-tech/react-native';
|
|
195
172
|
import { UserflowExpoRouterScreenTracker } from '@userflow-tech/react-native/expo-router';
|
|
196
173
|
import { Stack } from 'expo-router';
|
|
197
|
-
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
198
174
|
|
|
199
175
|
export default function RootLayout() {
|
|
200
176
|
useEffect(() => {
|
|
@@ -202,12 +178,10 @@ export default function RootLayout() {
|
|
|
202
178
|
}, []);
|
|
203
179
|
|
|
204
180
|
return (
|
|
205
|
-
<
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
</UserflowProvider>
|
|
210
|
-
</SafeAreaProvider>
|
|
181
|
+
<UserflowProvider>
|
|
182
|
+
<UserflowExpoRouterScreenTracker />
|
|
183
|
+
<Stack />
|
|
184
|
+
</UserflowProvider>
|
|
211
185
|
);
|
|
212
186
|
}
|
|
213
187
|
```
|
|
@@ -247,14 +221,14 @@ to the device's initial insets:
|
|
|
247
221
|
|
|
248
222
|
### Call order
|
|
249
223
|
|
|
250
|
-
| Call | Before `init()` resolves | Before `identify()`
|
|
251
|
-
| -------------------------------- | ------------------------------------------------------ |
|
|
252
|
-
| `identify` / `identifyAnonymous` | **Throws** `Call Userflow.init() before identify()` | —
|
|
253
|
-
| `group` | **Throws** `Call Userflow.init() before group()` | **Throws** `Call Userflow.identify() before group()`
|
|
254
|
-
| `updateUser` | **Throws** `Call Userflow.init() before updateUser()` | **Throws** `Call Userflow.identify() before updateUser()`
|
|
255
|
-
| `updateGroup` | **Throws** `Call Userflow.init() before updateGroup()` | **Throws** `Call Userflow.identify() before updateGroup()`; also requires a prior `group()`
|
|
256
|
-
| `track` | **Throws** `Call Userflow.init() before track()` | **Throws** `Call Userflow.identify() before track()`. Network, rate-limit, and unexpected SDK errors are isolated (the promise resolves; see [isolation.md](docs/isolation.md)) |
|
|
257
|
-
| `updateScreen` | Silently buffers; included on the next channel join | Silently buffers
|
|
224
|
+
| Call | Before `init()` resolves | Before `identify()` |
|
|
225
|
+
| -------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
226
|
+
| `identify` / `identifyAnonymous` | **Throws** `Call Userflow.init() before identify()` | — |
|
|
227
|
+
| `group` | **Throws** `Call Userflow.init() before group()` | **Throws** `Call Userflow.identify() before group()` |
|
|
228
|
+
| `updateUser` | **Throws** `Call Userflow.init() before updateUser()` | **Throws** `Call Userflow.identify() before updateUser()` |
|
|
229
|
+
| `updateGroup` | **Throws** `Call Userflow.init() before updateGroup()` | **Throws** `Call Userflow.identify() before updateGroup()`; also requires a prior `group()` |
|
|
230
|
+
| `track` | **Throws** `Call Userflow.init() before track()` | **Throws** `Call Userflow.identify() before track()`. Network, rate-limit, and unexpected SDK errors are isolated (the promise resolves; see [isolation.md](https://unpkg.com/@userflow-tech/react-native/docs/isolation.md)) |
|
|
231
|
+
| `updateScreen` | Silently buffers; included on the next channel join | Silently buffers |
|
|
258
232
|
|
|
259
233
|
Other rules:
|
|
260
234
|
|
|
@@ -264,14 +238,13 @@ Other rules:
|
|
|
264
238
|
that init is in flight or has succeeded logs
|
|
265
239
|
`[Userflow] Userflow.init() already ran with different options. The new clientToken, serverEndpoint, and pairingEndpoint are ignored because init runs once per process. Userflow.reset() does not apply new credentials. Restart the app to change them.`
|
|
266
240
|
Restart the app to change them after a successful init. If `init` rejects
|
|
267
|
-
(wrong token,
|
|
268
|
-
options; that retry runs. `Userflow.reset()` clears the signed-in user;
|
|
241
|
+
(wrong token), call it again with the corrected options; that retry runs. `Userflow.reset()` clears the signed-in user;
|
|
269
242
|
it does not apply new credentials.
|
|
270
243
|
- `identify()` with a **different** user id resets the previous identity first.
|
|
271
244
|
Same-id re-identify merges attributes.
|
|
272
245
|
- Identity verification `signature` is optional in the type, but **required by
|
|
273
246
|
the server** when the environment has identity verification enabled. See
|
|
274
|
-
[docs/identity-verification.md](docs/identity-verification.md).
|
|
247
|
+
[docs/identity-verification.md](https://unpkg.com/@userflow-tech/react-native/docs/identity-verification.md).
|
|
275
248
|
|
|
276
249
|
## Localization
|
|
277
250
|
|
|
@@ -304,7 +277,7 @@ unmount or crash the host tree. `init` / `identify` still throw — a swallowed
|
|
|
304
277
|
identify would look like success. Native crashes and errors in **your** children
|
|
305
278
|
of `UserflowProvider` are out of scope.
|
|
306
279
|
|
|
307
|
-
Details: [docs/isolation.md](docs/isolation.md).
|
|
280
|
+
Details: [docs/isolation.md](https://unpkg.com/@userflow-tech/react-native/docs/isolation.md).
|
|
308
281
|
|
|
309
282
|
## Screen tracking
|
|
310
283
|
|
|
@@ -320,22 +293,20 @@ re-fires the current screen.
|
|
|
320
293
|
|
|
321
294
|
Mount `UserflowExpoRouterScreenTracker` from the `@userflow-tech/react-native/expo-router`
|
|
322
295
|
subpath under `UserflowProvider` (the tracker is a separate entry so apps without
|
|
323
|
-
`expo-router` are not forced to install that peer)
|
|
296
|
+
`expo-router` are not forced to install that peer). Expo Router already renders
|
|
297
|
+
`SafeAreaProvider` above the root layout:
|
|
324
298
|
|
|
325
299
|
```tsx
|
|
326
300
|
import { UserflowProvider } from '@userflow-tech/react-native';
|
|
327
301
|
import { UserflowExpoRouterScreenTracker } from '@userflow-tech/react-native/expo-router';
|
|
328
302
|
import { Stack } from 'expo-router';
|
|
329
|
-
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
330
303
|
|
|
331
304
|
export default function RootLayout() {
|
|
332
305
|
return (
|
|
333
|
-
<
|
|
334
|
-
<
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
</UserflowProvider>
|
|
338
|
-
</SafeAreaProvider>
|
|
306
|
+
<UserflowProvider>
|
|
307
|
+
<UserflowExpoRouterScreenTracker />
|
|
308
|
+
<Stack />
|
|
309
|
+
</UserflowProvider>
|
|
339
310
|
);
|
|
340
311
|
}
|
|
341
312
|
```
|
|
@@ -397,13 +368,13 @@ function InviteScreen() {
|
|
|
397
368
|
|
|
398
369
|
Targets inside a `ScrollView` / `FlatList` are scrolled into view automatically.
|
|
399
370
|
`useUserflowScrollContainer` is only needed as an override for scroll libraries
|
|
400
|
-
the fiber walk cannot drive — see [docs/anchoring.md](docs/anchoring.md).
|
|
371
|
+
the fiber walk cannot drive — see [docs/anchoring.md](https://unpkg.com/@userflow-tech/react-native/docs/anchoring.md).
|
|
401
372
|
|
|
402
373
|
In the Userflow builder, set the MANUAL step selector CSS to `#invite-teammates`
|
|
403
374
|
(or `invite-teammates`). Use `useUserflowAnchor(id)` only when you need an
|
|
404
375
|
explicit override (e.g. a third-party component that does not accept `testID`).
|
|
405
376
|
Full contract, prop priority, missing-target behavior, and duplicate-id rules:
|
|
406
|
-
[docs/anchoring.md](docs/anchoring.md).
|
|
377
|
+
[docs/anchoring.md](https://unpkg.com/@userflow-tech/react-native/docs/anchoring.md).
|
|
407
378
|
|
|
408
379
|
## Builder preview & pairing
|
|
409
380
|
|
|
@@ -413,8 +384,7 @@ To preview draft flows from the Userflow builder onto a device (QR / deep link):
|
|
|
413
384
|
`userflow-v7twv2fw2ja2jaco7kesvowmem` — **not** the env UUID) with the
|
|
414
385
|
`@userflow-tech/react-native` config plugin, then **rebuild native**
|
|
415
386
|
(`npx expo prebuild` / a new development client). Metro reload is not enough.
|
|
416
|
-
2. Call `Userflow.init`
|
|
417
|
-
for staging or local stacks) and wrap the tree in `UserflowProvider`.
|
|
387
|
+
2. Call `Userflow.init` and wrap the tree in `UserflowProvider`.
|
|
418
388
|
3. Open the builder Preview dialog → **Pair a device** → scan/open the QR. The
|
|
419
389
|
app should show a **Preview mode** top bar.
|
|
420
390
|
4. Hit **Preview** in the builder. The draft flow lands on that paired device.
|
|
@@ -429,15 +399,6 @@ To preview draft flows from the Userflow builder onto a device (QR / deep link):
|
|
|
429
399
|
}
|
|
430
400
|
```
|
|
431
401
|
|
|
432
|
-
```ts
|
|
433
|
-
await Userflow.init({
|
|
434
|
-
clientToken: 'ct_...',
|
|
435
|
-
// Local Userflow stack example:
|
|
436
|
-
// serverEndpoint: 'http://localhost:40401',
|
|
437
|
-
// pairingEndpoint: 'http://localhost:40401',
|
|
438
|
-
});
|
|
439
|
-
```
|
|
440
|
-
|
|
441
402
|
Deep-link / pairing helpers:
|
|
442
403
|
|
|
443
404
|
| Member | Description |
|
|
@@ -448,8 +409,8 @@ Deep-link / pairing helpers:
|
|
|
448
409
|
| `PreviewModeBanner` | Mounted by `UserflowProvider` while paired. The top bar shows at once for a preview link, stays hidden for an element-selection link and after any pick until the next preview, and waits briefly on a link with no purpose. |
|
|
449
410
|
|
|
450
411
|
Full scheme registration, Linking ownership, and verify commands:
|
|
451
|
-
[docs/deep-linking.md](docs/deep-linking.md). Symptom table:
|
|
452
|
-
[docs/troubleshooting.md](docs/troubleshooting.md
|
|
412
|
+
[docs/deep-linking.md](https://unpkg.com/@userflow-tech/react-native/docs/deep-linking.md). Symptom table: the "Builder preview / pairing" section of
|
|
413
|
+
[docs/troubleshooting.md](https://unpkg.com/@userflow-tech/react-native/docs/troubleshooting.md).
|
|
453
414
|
|
|
454
415
|
## Connection resilience
|
|
455
416
|
|
|
@@ -480,33 +441,35 @@ Screen tracking ≠ action routing. React Navigation apps need
|
|
|
480
441
|
|
|
481
442
|
### `Userflow`
|
|
482
443
|
|
|
483
|
-
| Member
|
|
484
|
-
|
|
|
485
|
-
| `init(config)`
|
|
486
|
-
| `identify(userId, attributes?, options?)`
|
|
487
|
-
| `identifyAnonymous(attributes?, options?)`
|
|
488
|
-
| `group(groupId, attributes?, options?)`
|
|
489
|
-
| `updateUser(attributes?)`
|
|
490
|
-
| `updateGroup(attributes?, options?)`
|
|
491
|
-
| `track(name, attributes?, options?)`
|
|
492
|
-
| `reset()`
|
|
493
|
-
| `teardown()`
|
|
494
|
-
| `updateScreen(name)`
|
|
495
|
-
| `setOnNavigate(handler)`
|
|
496
|
-
| `reportActivity()`
|
|
497
|
-
| `didHandleURL(url)`
|
|
498
|
-
| `handlePairingUrl(url)`
|
|
499
|
-
| `exitPreview()`
|
|
500
|
-
| `
|
|
501
|
-
| `
|
|
502
|
-
| `
|
|
503
|
-
| `
|
|
504
|
-
| `
|
|
505
|
-
| `
|
|
506
|
-
| `
|
|
507
|
-
| `
|
|
508
|
-
| `
|
|
509
|
-
| `
|
|
444
|
+
| Member | Description |
|
|
445
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
446
|
+
| `init(config)` | Validate credentials, store config, start lifecycle + network controllers. |
|
|
447
|
+
| `identify(userId, attributes?, options?)` | Identify a user; auto-sets `sdk` / `device_os` / `device_type` and `app_version` / `app_build` from init; `options.signature` for identity verification. |
|
|
448
|
+
| `identifyAnonymous(attributes?, options?)` | Stable anonymous id via AsyncStorage; auto-sets `sdk` / `device_os` / `device_type` and `app_version` / `app_build`. |
|
|
449
|
+
| `group(groupId, attributes?, options?)` | Associate the current user with a group. |
|
|
450
|
+
| `updateUser(attributes?)` | Upsert attributes for the current user without re-identifying. |
|
|
451
|
+
| `updateGroup(attributes?, options?)` | Upsert group / membership attributes without re-grouping. |
|
|
452
|
+
| `track(name, attributes?, options?)` | Record a custom event (literal attribute values only); `userOnly` skips group. Misuse (`UserflowAuthError`) rejects; network / rate-limit / SDK bugs are isolated. |
|
|
453
|
+
| `reset()` | Clear identity, the stored anonymous id, client conditions, and flow and banner sessions; disconnect. |
|
|
454
|
+
| `teardown()` | Stop AppState / NetInfo / screen-tracking subscriptions. |
|
|
455
|
+
| `updateScreen(name)` | Report the active screen (prefer hooks when possible). |
|
|
456
|
+
| `setOnNavigate(handler)` | Override or clear the host NAVIGATE handler. |
|
|
457
|
+
| `reportActivity()` | Reset the idle timer; reconnect an idle-suspended socket. |
|
|
458
|
+
| `didHandleURL(url)` | Handle a Userflow preview deep link (`…://?token=…`). |
|
|
459
|
+
| `handlePairingUrl(url)` | Handle a Builder pairing deep link (`…://?pairing_token=…`). Works in every build; the signed JWT gates the session. |
|
|
460
|
+
| `exitPreview()` | Leave Builder preview mode; disconnect the pairing websocket. Works in every build. |
|
|
461
|
+
| `setScrollPadding(padding)` | Sticky header/footer insets used when scrolling tooltip targets into view. |
|
|
462
|
+
| `setCustomScrollIntoView(fn)` | Replace scroll-into-view (e.g. unsupported scroll libraries). Pass `null` to restore the default. |
|
|
463
|
+
| `setSurfaceInteractionBlocking(enabled)` | `true` (default) blocks taps on the app under tooltip backdrops. `false` lets them through. Modals always block. |
|
|
464
|
+
| `setEvalJsContext(values)` | Values exposed to Evaluate JavaScript as `window.userflowContext.host`. Replaces the previous values. |
|
|
465
|
+
| `getUserId()` | Current user id, or `null` before identify. |
|
|
466
|
+
| `getAttributes()` | Copy of attributes for the current user. |
|
|
467
|
+
| `getFlowSessionEngine()` | Mid-flow session engine (created lazily; attach runs after identify). |
|
|
468
|
+
| `getBannerSessionEngine()` | Banner session engine (created lazily; attach runs after identify). |
|
|
469
|
+
| `getHostAppVersion()` | Host app native version cached at init, or `null` before init / when unavailable. |
|
|
470
|
+
| `getDeviceContext()` | Platform metadata cached during `init` (`sdk`, `deviceOs`, `deviceType`), or `null` before init. |
|
|
471
|
+
| `logHostAppVersion()` | Log the host app's native version (not `SDK_VERSION`) and platform OS. Uses bundled device-info on bare RN. |
|
|
472
|
+
| `logDeviceContext()` | Log cached user-attribute values (`sdk` / `device_os` / `device_type`). Requires `init`. |
|
|
510
473
|
|
|
511
474
|
### Hooks and components
|
|
512
475
|
|
|
@@ -519,8 +482,19 @@ Screen tracking ≠ action routing. React Navigation apps need
|
|
|
519
482
|
| `useUserflowScreen(name)` | Report the active screen from a React tree. |
|
|
520
483
|
| `useUserflowAnchor(id)` | Optional override: register a view as a MANUAL target (beats `userflowId` / `testID`). |
|
|
521
484
|
| `useUserflowScrollContainer(onScroll?)` | Optional override: register a `ScrollView` for tooltip scroll-into-view. |
|
|
522
|
-
| `
|
|
523
|
-
|
|
485
|
+
| `EmbedBannerPlaceholder` | Slot for a banner placed on an element (`<EmbedBannerPlaceholder id="welcome-banner" />` matches the selector `#welcome-banner`). Renders nothing until a matching banner is active. Top and bottom banners ignore it. |
|
|
486
|
+
|
|
487
|
+
### Pairing helpers
|
|
488
|
+
|
|
489
|
+
Named exports, not members of `Userflow`. Use them when your app owns `Linking`
|
|
490
|
+
and forwards URLs itself.
|
|
491
|
+
|
|
492
|
+
| Export | Description |
|
|
493
|
+
| ----------------------------- | ------------------------------------------------------------------------- |
|
|
494
|
+
| `inspectPairingDeepLink(url)` | Classify a URL as pairing without consuming it; reports rejection reason. |
|
|
495
|
+
| `isPairingAttempt(reason)` | True when a rejection was a recognisable pairing attempt worth reporting. |
|
|
496
|
+
| `parsePairingDeepLink(url)` | Parse a pairing deep link. |
|
|
497
|
+
| `isUserflowPairingUrl(url)` | Detect a pairing deep link. |
|
|
524
498
|
|
|
525
499
|
### Subpath exports
|
|
526
500
|
|
|
@@ -529,31 +503,24 @@ Screen tracking ≠ action routing. React Navigation apps need
|
|
|
529
503
|
| `@userflow-tech/react-native/navigation` | `WithUserflowReactNavigation` |
|
|
530
504
|
| `@userflow-tech/react-native/expo-router` | `UserflowExpoRouterScreenTracker`, `useUserflowExpoRouter`, `redirectSystemPath` (for `+native-intent`) |
|
|
531
505
|
|
|
532
|
-
### Constants
|
|
533
|
-
|
|
534
|
-
| Export | Meaning |
|
|
535
|
-
| ----------------------------------- | ---------------------------------------------------------------- |
|
|
536
|
-
| `SDK_VERSION` | Package version string injected at build time. |
|
|
537
|
-
| `SUSPEND_GRACE_MS` | Grace window before background suspend (3s). |
|
|
538
|
-
| `SESSION_STALE_AFTER_BACKGROUND_MS` | Clear mid-flow session after this long away (5 min). |
|
|
539
|
-
| `INACTIVITY_TIMEOUT_MS` | Foreground idle suspend (5 min; skipped while a flow is active). |
|
|
540
|
-
|
|
541
506
|
## Guides in this package
|
|
542
507
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
|
547
|
-
|
|
|
548
|
-
| [docs/
|
|
549
|
-
| [docs/
|
|
550
|
-
| [docs/
|
|
508
|
+
These guides also ship in the package, under
|
|
509
|
+
`node_modules/@userflow-tech/react-native/docs/`.
|
|
510
|
+
|
|
511
|
+
| Doc | Topic |
|
|
512
|
+
| ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- |
|
|
513
|
+
| [docs/anchoring.md](https://unpkg.com/@userflow-tech/react-native/docs/anchoring.md) | Manual targets, scroll containers, missing-target behavior |
|
|
514
|
+
| [docs/isolation.md](https://unpkg.com/@userflow-tech/react-native/docs/isolation.md) | What “isolated as possible” means; overlay + `track` guards |
|
|
515
|
+
| [docs/deep-linking.md](https://unpkg.com/@userflow-tech/react-native/docs/deep-linking.md) | Builder preview URLs and pairing sessions |
|
|
516
|
+
| [docs/identity-verification.md](https://unpkg.com/@userflow-tech/react-native/docs/identity-verification.md) | HMAC signature on your backend |
|
|
517
|
+
| [docs/troubleshooting.md](https://unpkg.com/@userflow-tech/react-native/docs/troubleshooting.md) | Symptom → cause for common integration failures |
|
|
551
518
|
|
|
552
519
|
## Experimental
|
|
553
520
|
|
|
554
521
|
- AUTO / codeless element targeting (exact-id fast path + scored matcher;
|
|
555
522
|
scored path needs Babel full mode to populate the auto-anchor registry).
|
|
556
|
-
See [docs/anchoring.md](docs/anchoring.md).
|
|
523
|
+
See [docs/anchoring.md](https://unpkg.com/@userflow-tech/react-native/docs/anchoring.md).
|
|
557
524
|
- Build-time stamping with `@userflow-tech/react-native/babel-plugin`. Jest
|
|
558
525
|
runs the plugin too, which changes snapshots. Read
|
|
559
526
|
[Babel plugin and snapshot tests](#babel-plugin-and-snapshot-tests) before
|
|
@@ -591,10 +558,6 @@ Passing an environment secret key as `clientToken` throws:
|
|
|
591
558
|
clientToken is an environment secret key (esk_...). That key must never ship in the app. Pass the client token (ct_...) from Settings → Environments instead.
|
|
592
559
|
```
|
|
593
560
|
|
|
594
|
-
When the default US endpoint rejects the token with `invalid_client_token`, the
|
|
595
|
-
error also says: `For EU-hosted environments pass serverEndpoint: 'https://e.eu.userflow.com'.`
|
|
596
|
-
Pairing stays on `https://e.userflow.com`.
|
|
597
|
-
|
|
598
561
|
`expo prebuild` warns when the config plugin has no `schemeId`, when
|
|
599
562
|
`react-native-view-shot` is missing or older than 5, and (on Expo SDK 53 and
|
|
600
563
|
older) when AsyncStorage, WebView, or safe-area-context is not a direct
|
|
@@ -709,4 +672,4 @@ the files it lists, not the components a test renders.
|
|
|
709
672
|
- `INPUT_VALUE` / `FILLED_IN_INPUT` client conditions
|
|
710
673
|
|
|
711
674
|
Flows gated on unsupported client conditions stay unknown and **never start**.
|
|
712
|
-
See [docs/troubleshooting.md](docs/troubleshooting.md).
|
|
675
|
+
See [docs/troubleshooting.md](https://unpkg.com/@userflow-tech/react-native/docs/troubleshooting.md).
|
package/docs/anchoring.md
CHANGED
|
@@ -16,7 +16,7 @@ Dashboard MANUAL selectors store a CSS-like string on `step.selector.css` (or
|
|
|
16
16
|
`condition.selector.css`). The SDK resolves it with:
|
|
17
17
|
|
|
18
18
|
```typescript
|
|
19
|
-
//
|
|
19
|
+
// src/anchors/resolveManualAnchorId.ts
|
|
20
20
|
return trimmed.startsWith('#') ? trimmed.slice(1) : trimmed;
|
|
21
21
|
```
|
|
22
22
|
|
|
@@ -318,7 +318,7 @@ Modals always block — the full-screen backdrop is a `Pressable` hit target.
|
|
|
318
318
|
Pass `Userflow.setSurfaceInteractionBlocking(false)` to restore the legacy
|
|
319
319
|
pass-through tooltip backdrop (`pointerEvents="none"` on dim bands) so the
|
|
320
320
|
host screen stays scrollable and tappable underneath. The `tooltipTargetBlocked`
|
|
321
|
-
hole seal is unchanged
|
|
321
|
+
hole seal is unchanged.
|
|
322
322
|
|
|
323
323
|
When a tooltip step has `backdrop: false`, the cutout still mounts with dim
|
|
324
324
|
opacity `0` so interaction blocking applies without a visible dim.
|
|
@@ -341,7 +341,7 @@ rect** via a pass-through bridge around app content (`TargetPressBridge` in
|
|
|
341
341
|
ELEMENT `CLICK` / `NOT_CLICK` auto-start conditions use the same bridge
|
|
342
342
|
(`elementTapMonitor`) and do not steal the press either. `MOUSEDOWN` and
|
|
343
343
|
`DISABLED` conditions are still unsupported — see
|
|
344
|
-
[
|
|
344
|
+
[troubleshooting.md](troubleshooting.md).
|
|
345
345
|
|
|
346
346
|
## Missing-target behavior
|
|
347
347
|
|
|
@@ -404,5 +404,3 @@ The Userflow overlay is a _sibling_ of the scan root, so SDK-owned `testID`s
|
|
|
404
404
|
## See also
|
|
405
405
|
|
|
406
406
|
- [troubleshooting.md](troubleshooting.md) — “flow never appeared” checklist
|
|
407
|
-
- [theme-mapping.md](theme-mapping.md) — chrome tokens that affect tooltip UI
|
|
408
|
-
- Repo `docs/client-conditions.md` (monorepo only) — track / toggle protocol internals
|
package/docs/deep-linking.md
CHANGED
|
@@ -147,9 +147,6 @@ Rebuild the native app after changing these files.
|
|
|
147
147
|
```ts
|
|
148
148
|
await Userflow.init({
|
|
149
149
|
clientToken: 'ct_...',
|
|
150
|
-
// Defaults to production. Override for staging / local stacks:
|
|
151
|
-
// serverEndpoint: 'http://localhost:40401',
|
|
152
|
-
// pairingEndpoint: 'http://localhost:40401',
|
|
153
150
|
});
|
|
154
151
|
await Userflow.identify(userId); // or identifyAnonymous()
|
|
155
152
|
```
|
|
@@ -293,19 +290,5 @@ conditions to match, so nothing appears until they do — the top bar reads
|
|
|
293
290
|
**Waiting for auto-start condition…** meanwhile. A flow that never
|
|
294
291
|
shows up in that mode points at the condition, not at pairing.
|
|
295
292
|
|
|
296
|
-
## Local / staging endpoints
|
|
297
|
-
|
|
298
|
-
Defaults target production (`https://e.userflow.com` for the end-user socket;
|
|
299
|
-
pairing defaults to the US-pinned production host). When pointing at a local or
|
|
300
|
-
staging stack, set both:
|
|
301
|
-
|
|
302
|
-
```ts
|
|
303
|
-
await Userflow.init({
|
|
304
|
-
clientToken: 'ct_...',
|
|
305
|
-
serverEndpoint: 'http://localhost:40401',
|
|
306
|
-
pairingEndpoint: 'http://localhost:40401',
|
|
307
|
-
});
|
|
308
|
-
```
|
|
309
|
-
|
|
310
293
|
See [troubleshooting.md](troubleshooting.md#builder-preview--pairing) for common
|
|
311
294
|
pairing failures.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -7,9 +7,6 @@ Messages are quoted exactly so you can search logs for them.
|
|
|
7
7
|
|
|
8
8
|
| Symptom / log message | Likely cause | Fix |
|
|
9
9
|
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
10
|
-
| `404` from `npm install` or `npm view @userflow-tech/react-native` | The npm token is missing, or the npm user or org is not on the read-only `@userflow-tech` team. npm returns 404 for a private package the caller cannot see. | Ask Userflow to add the npm user or org to the read-only team, then create a granular read-only token limited to `@userflow-tech`. |
|
|
11
|
-
| Yarn 2+ still 404s with a valid `.npmrc` | Yarn 2 and later ignore `.npmrc`. | Set `npmScopes.userflow-tech.npmRegistryServer` and `npmAuthToken` in `.yarnrc.yml`. |
|
|
12
|
-
| EAS or CI install 404s while local install works | `NPM_TOKEN` is not available to that environment. | Add `NPM_TOKEN` as an EAS secret or CI secret. The `.npmrc` line `//registry.npmjs.org/:_authToken=${NPM_TOKEN}` reads it at install time. |
|
|
13
10
|
| `expo doctor` reports `react-native-view-shot` | The app pins `^5.0.0` and Expo SDK 54's expected version is 4.0.3. | Keep the pin. Add `"react-native-view-shot"` to `expo.install.exclude`. |
|
|
14
11
|
| `Unable to resolve module` for an optional peer (`netinfo`, `expo-secure-store`, `expo-router`, safe-area, navigation) | A custom Metro config does not extend `getDefaultConfig`, so `allowOptionalDependencies` is off. | Extend `getDefaultConfig` from `expo/metro-config` or `@react-native/metro-config`. |
|
|
15
12
|
| `ERESOLVE` while installing `@userflow-tech/react-native` | The app already has a version outside a peer range: view-shot 4.0.3 (what unpinned `npx expo install` installs on Expo SDK 54), NetInfo 9.x, safe-area-context 3.x, WebView 13.6.x or 14.x, or React Native older than 0.76. | Move that package into the range in the package README. For view-shot, npm prints `Found: react-native-view-shot@4.0.3` and `peer react-native-view-shot@"^5.0.0" from @userflow-tech/react-native`. Install the SDK and `react-native-view-shot@^5.0.0` in one command, then add `"react-native-view-shot"` to `expo.install.exclude` on Expo SDK 54. Do not retry with `--force` or `--legacy-peer-deps`; that leaves 4.0.3 installed. Raise React Native to 0.76 or later when that is the conflict. |
|
|
@@ -32,7 +29,6 @@ Messages are quoted exactly so you can search logs for them.
|
|
|
32
29
|
| `[Userflow] UserflowProvider is not inside a <SafeAreaProvider>, so flow cards use the device's initial safe-area insets. Wrap your app in <SafeAreaProvider> from react-native-safe-area-context.` | `UserflowProvider` has no `SafeAreaProvider` ancestor. The app does not crash. Cards use `initialWindowMetrics`, or zero insets when those are unavailable. | Wrap the tree in `SafeAreaProvider`. |
|
|
33
30
|
| `Userflow.identifyAnonymous() needs @react-native-async-storage/async-storage, which is not linked in this native build. Add it to your app's package.json, run pod install, and rebuild.` | `identifyAnonymous()` ran without a linked AsyncStorage native module. | Install and rebuild, then call it again. |
|
|
34
31
|
| `clientToken is an environment secret key (esk_...). That key must never ship in the app. Pass the client token (ct_...) from Settings → Environments instead.` | `init` was given an `esk_` secret key. | Pass the `ct_…` client token. |
|
|
35
|
-
| `For EU-hosted environments pass serverEndpoint: 'https://e.eu.userflow.com'.` | The default US host rejected the token with `invalid_client_token`. This sentence is appended to that error. It is omitted when `serverEndpoint` was set explicitly. | For an EU environment, pass `serverEndpoint: 'https://e.eu.userflow.com'`. Pairing stays on `https://e.userflow.com`. |
|
|
36
32
|
| `[Userflow] Userflow.init() already ran with different options. The new clientToken, serverEndpoint, and pairingEndpoint are ignored because init runs once per process. Userflow.reset() does not apply new credentials. Restart the app to change them.` | A later `init` passed a different token or endpoint while a previous `init` is in flight or has succeeded. | Restart the app after a successful init. `reset()` clears the user and does not apply new credentials. If the previous `init` rejected, call `init` again with the corrected token or endpoint. |
|
|
37
33
|
|
|
38
34
|
## Init / identify / group
|
|
@@ -46,7 +42,7 @@ Messages are quoted exactly so you can search logs for them.
|
|
|
46
42
|
| `Call Userflow.identify() before updateUser()` | `updateUser` before any identify | Call `identify` or `identifyAnonymous` first |
|
|
47
43
|
| `Call Userflow.group() before updateGroup()` | `updateGroup` or a group-scoped button attribute before `group()` | Call `Userflow.group(...)` first |
|
|
48
44
|
| `Missing clientToken` | Empty / whitespace token | Pass the `ct_...` value from the dashboard |
|
|
49
|
-
| `… invalid_client_token: Invalid Userflow.js Token: …` | Wrong token
|
|
45
|
+
| `… invalid_client_token: Invalid Userflow.js Token: …` | Wrong token or deactivated env | Copy the token from the matching environment. |
|
|
50
46
|
| `… user_signature_required: …` | Env requires identity verification; no `signature` | Compute HMAC on your backend — [identity-verification.md](identity-verification.md) |
|
|
51
47
|
| `… incorrect_user_signature: …` | Signature does not match HMAC-SHA256(secret, userId) | Sign the **same** id string; use `esk_...`, not `ct_...` |
|
|
52
48
|
| `… group_signature_required` / `incorrect_group_signature` | Same rules for `group()` | Sign the group id with the env secret key |
|
|
@@ -107,7 +103,7 @@ Id matching: dashboard `#invite-teammates` ↔ `userflowId="invite-teammates"`,
|
|
|
107
103
|
|
|
108
104
|
| Symptom | Likely cause |
|
|
109
105
|
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
110
|
-
| `WebSocket connection timed out` / `failed` |
|
|
106
|
+
| `WebSocket connection timed out` / `failed` | Network or VPN |
|
|
111
107
|
| `invalid_message` on channel join / `UpdateClientContext` | ClientContext missing required `pageUrl`, or sending `screenPath` / legacy `path`/`screen`. The SDK must send `pageUrl: ""` plus `screenName` only |
|
|
112
108
|
| Long recovery after Wi-Fi ↔ cellular while foregrounded | Install optional `@react-native-community/netinfo` for instant probe |
|
|
113
109
|
| Session cleared after returning from background | Away ≥ `SESSION_STALE_AFTER_BACKGROUND_MS` (5 minutes) — by design |
|
|
@@ -116,26 +112,25 @@ Id matching: dashboard `#invite-teammates` ↔ `userflowId="invite-teammates"`,
|
|
|
116
112
|
|
|
117
113
|
Full setup: [deep-linking.md](deep-linking.md). Pairing is available in all builds (including release).
|
|
118
114
|
|
|
119
|
-
| Symptom | Likely cause | Fix
|
|
120
|
-
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
|
|
121
|
-
| Deep link opens the app but nothing happens | Scheme added in config but **no native rebuild** | `npx expo prebuild` / new development client; Metro reload is not enough
|
|
122
|
-
| Deep link opens the app but nothing happens, and no **Preview mode** top bar appears | `Userflow.init()` rejected, so there is no session to preview into | Check the logs for `[Userflow] Builder pairing link received, but Userflow.init() failed` — fix that error (usually `clientToken`
|
|
123
|
-
| Killed app opens from a pairing link with no banner, or Preview returns on the wrong step | Pairing did not resume, or the install joined as a new device while the old presence was still on the topic | Install `expo-secure-store` so a relaunch can resume. A cold start should show **Connecting to Builder…** immediately, then **Preview mode**, with the flow at step 1. `exitPreview()` or an expired token ends resume
|
|
124
|
-
| Deep link opens the app and the router shows **Unmatched Route** / "page could not be found" | Deep link is not the root form from the QR; **or** the link was rebuilt with the app's own scheme instead of `scheme_id` | Open the QR unmodified (`userflow-<id>://?pairing_token=…`). If the log says `a scheme Userflow does not own`, do not substitute your app scheme
|
|
125
|
-
| "Can't select this element" right after pairing, or the app jumps to home when pairing | Expo Router navigated on the pairing link | Add `app/+native-intent.ts` exporting `redirectSystemPath` from `@userflow-tech/react-native/expo-router` (see [deep-linking.md](deep-linking.md#expo-router-hosts))
|
|
126
|
-
| Pairing deep link ignored (preview URL may still work) | Scheme is `userflow-*` but the id after it is empty or has stray hyphens | Use the env's `scheme_id` from the dashboard exactly (e.g. `userflow-v7twv2fw2ja2jaco7kesvowmem`), not the env UUID
|
|
127
|
-
| Builder says “A device was paired earlier…” and Preview closes immediately after a brief spinner | Stale pairing session still present; a ghost device acks | Pair again on the device you are watching, or clear the env's pairing session and re-QR
|
|
128
|
-
| Preview succeeds on the server but the phone stays idle | App is not the paired instance (no **Preview mode** top bar) | Confirm the top bar before hitting Preview; re-open the pairing QR on this install
|
|
129
|
-
|
|
|
130
|
-
|
|
|
131
|
-
| Builder selector has no screenshot after Confirm | Missing / unlinked `react-native-view-shot`, or Expo SDK 54 pinned 4.x | Install `react-native-view-shot@^5.0.0` and **rebuild native**. Look for `[Userflow] Required peer react-native-view-shot is not installed` |
|
|
115
|
+
| Symptom | Likely cause | Fix |
|
|
116
|
+
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
117
|
+
| Deep link opens the app but nothing happens | Scheme added in config but **no native rebuild** | `npx expo prebuild` / new development client; Metro reload is not enough |
|
|
118
|
+
| Deep link opens the app but nothing happens, and no **Preview mode** top bar appears | `Userflow.init()` rejected, so there is no session to preview into | Check the logs for `[Userflow] Builder pairing link received, but Userflow.init() failed` — fix that error (usually `clientToken`) and re-open the link |
|
|
119
|
+
| Killed app opens from a pairing link with no banner, or Preview returns on the wrong step | Pairing did not resume, or the install joined as a new device while the old presence was still on the topic | Install `expo-secure-store` so a relaunch can resume. A cold start should show **Connecting to Builder…** immediately, then **Preview mode**, with the flow at step 1. `exitPreview()` or an expired token ends resume |
|
|
120
|
+
| Deep link opens the app and the router shows **Unmatched Route** / "page could not be found" | Deep link is not the root form from the QR; **or** the link was rebuilt with the app's own scheme instead of `scheme_id` | Open the QR unmodified (`userflow-<id>://?pairing_token=…`). If the log says `a scheme Userflow does not own`, do not substitute your app scheme |
|
|
121
|
+
| "Can't select this element" right after pairing, or the app jumps to home when pairing | Expo Router navigated on the pairing link | Add `app/+native-intent.ts` exporting `redirectSystemPath` from `@userflow-tech/react-native/expo-router` (see [deep-linking.md](deep-linking.md#expo-router-hosts)) |
|
|
122
|
+
| Pairing deep link ignored (preview URL may still work) | Scheme is `userflow-*` but the id after it is empty or has stray hyphens | Use the env's `scheme_id` from the dashboard exactly (e.g. `userflow-v7twv2fw2ja2jaco7kesvowmem`), not the env UUID |
|
|
123
|
+
| Builder says “A device was paired earlier…” and Preview closes immediately after a brief spinner | Stale pairing session still present; a ghost device acks | Pair again on the device you are watching, or clear the env's pairing session and re-QR |
|
|
124
|
+
| Preview succeeds on the server but the phone stays idle | App is not the paired instance (no **Preview mode** top bar) | Confirm the top bar before hitting Preview; re-open the pairing QR on this install |
|
|
125
|
+
| QR / `https://…/pair/<jwt>` does nothing in the app | SDK only parses the **scheme** form after the backend redirect | Open via the OS (scan QR) or forward `{schemeId}://?pairing_token=…` with `handlePairingUrl` |
|
|
126
|
+
| Builder selector has no screenshot after Confirm | Missing / unlinked `react-native-view-shot`, or Expo SDK 54 pinned 4.x | Install `react-native-view-shot@^5.0.0` and **rebuild native**. At init, look for `- react-native-view-shot is not linked, so Builder element selection gets no screenshot`. When confirming a pick, look for `[Userflow] Required peer react-native-view-shot is not installed; skipping picker screenshot.` |
|
|
132
127
|
|
|
133
128
|
## Expo
|
|
134
129
|
|
|
135
130
|
| Symptom | Fix |
|
|
136
131
|
| ----------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
137
132
|
| Crashes or missing native modules in Expo Go | Use a **development build**; Expo Go is not supported |
|
|
138
|
-
| Duplicate React / RN instance crashes when linking local packages |
|
|
133
|
+
| Duplicate React / RN instance crashes when linking local packages | Make Metro resolve `react` and `react-native` from the app's own `node_modules` |
|
|
139
134
|
| Preview / pairing scheme not registered | Add the `@userflow-tech/react-native` plugin with `schemeId`, then rebuild native |
|
|
140
135
|
|
|
141
136
|
## Jest
|
package/lib/commonjs/version.js
CHANGED
|
@@ -5,5 +5,5 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.SDK_VERSION = void 0;
|
|
7
7
|
/** Generated by scripts/write-version.mjs — do not edit. */
|
|
8
|
-
const SDK_VERSION = exports.SDK_VERSION = '0.4.
|
|
8
|
+
const SDK_VERSION = exports.SDK_VERSION = '0.4.2';
|
|
9
9
|
//# sourceMappingURL=version.js.map
|
package/lib/module/version.js
CHANGED
package/package.json
CHANGED
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Generated by scripts/write-version.mjs — do not edit. */
|
|
2
|
-
export const SDK_VERSION = '0.4.
|
|
2
|
+
export const SDK_VERSION = '0.4.2';
|
|
@@ -4,10 +4,6 @@
|
|
|
4
4
|
*
|
|
5
5
|
* For local development against the Userflow dev stack, pass the local endpoint
|
|
6
6
|
* explicitly, e.g. `init({ ..., serverEndpoint: 'http://localhost:40401' })`.
|
|
7
|
-
*
|
|
8
|
-
* TODO(publish): production server-driven `Publish` delivery for mobile is not
|
|
9
|
-
* wired yet (only the Preview/dev-device path is validated). Revisit this
|
|
10
|
-
* default and the transport once Publish lands.
|
|
11
7
|
*/
|
|
12
8
|
export declare const DEFAULT_SERVER_ENDPOINT = "https://e.userflow.com";
|
|
13
9
|
/** End-user socket for EU-hosted environments. Pairing stays on the US host. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,2BAA2B,CAAC;AAEhE,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,2BAA2B,CAAC"}
|
|
@@ -4,10 +4,6 @@
|
|
|
4
4
|
*
|
|
5
5
|
* For local development against the Userflow dev stack, pass the local endpoint
|
|
6
6
|
* explicitly, e.g. `init({ ..., serverEndpoint: 'http://localhost:40401' })`.
|
|
7
|
-
*
|
|
8
|
-
* TODO(publish): production server-driven `Publish` delivery for mobile is not
|
|
9
|
-
* wired yet (only the Preview/dev-device path is validated). Revisit this
|
|
10
|
-
* default and the transport once Publish lands.
|
|
11
7
|
*/
|
|
12
8
|
export const DEFAULT_SERVER_ENDPOINT = 'https://e.userflow.com';
|
|
13
9
|
/** End-user socket for EU-hosted environments. Pairing stays on the US host. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,wBAAwB,CAAC;AAEhE,gFAAgF;AAChF,MAAM,CAAC,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,wBAAwB,CAAC"}
|
package/vendor/core/package.json
CHANGED
package/docs/theme-mapping.md
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
# Theme mapping (web → React Native)
|
|
2
|
-
|
|
3
|
-
Phase 3 maps Userflow web theme tokens onto React Native styles used by flow chrome.
|
|
4
|
-
This is an interim visual baseline aligned with the mobile tooltip/modal design specs.
|
|
5
|
-
|
|
6
|
-
## Card chrome — `resolveBubbleChrome`
|
|
7
|
-
|
|
8
|
-
Source: `src/surfaces/themeChrome.ts`
|
|
9
|
-
|
|
10
|
-
| Web / theme token | RN usage |
|
|
11
|
-
| ----------------------------------------- | ---------------------------------------------------------------- |
|
|
12
|
-
| `backgroundColor` | Card `backgroundColor` |
|
|
13
|
-
| `bubbleBorderRadius` (default `10`) | Card `borderRadius` |
|
|
14
|
-
| `bubbleBorderWidth` / `bubbleBorderColor` | Card border |
|
|
15
|
-
| `tooltipNotchSize` (default `10`) | Tooltip notch size; `0` hides notch |
|
|
16
|
-
| Modal content padding | `24` (mobile; web default is 40) |
|
|
17
|
-
| Tooltip / bubble content padding | `24` (web tooltip) |
|
|
18
|
-
| Shadow | Approximate dual drop-shadow via `shadow*` + Android `elevation` |
|
|
19
|
-
|
|
20
|
-
Modal cards use `overflow: 'hidden'` so the progress bar clips to the radius.
|
|
21
|
-
Tooltip cards use `overflow: 'visible'` so the notch is not clipped.
|
|
22
|
-
|
|
23
|
-
## Progress — `ProgressBar`
|
|
24
|
-
|
|
25
|
-
| Theme token | RN usage |
|
|
26
|
-
| ---------------------------------------- | ------------------------------------------------- |
|
|
27
|
-
| `progressBarEnabled` | Show / hide |
|
|
28
|
-
| `progressBarPosition` (`TOP` / `BOTTOM`) | Placement flush on the card edge |
|
|
29
|
-
| `progressBarType` | `DEFAULT` fraction bar; `NUMBERED` shows `1 of N` |
|
|
30
|
-
| Step `progress` + step index | Fraction via `resolveStepProgress` |
|
|
31
|
-
|
|
32
|
-
## Buttons — `StepButton`
|
|
33
|
-
|
|
34
|
-
| Theme token | RN usage |
|
|
35
|
-
| ------------------------------- | ---------------------------------------------------------------- |
|
|
36
|
-
| `primaryButton*` / widget brand | Primary filled button |
|
|
37
|
-
| Secondary (DEFAULT) | Neutral outline: white fill, `#d1d5db` border, `fontColor` label |
|
|
38
|
-
| Button fragment colors when set | Override defaults per button |
|
|
39
|
-
|
|
40
|
-
## Backdrop & cutout
|
|
41
|
-
|
|
42
|
-
| Theme token | RN usage |
|
|
43
|
-
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
|
44
|
-
| `backdropColor` | Modal full-screen backdrop; tooltip cutout fill |
|
|
45
|
-
| `backdropOpacity` | Parsed float (`parseBackdropOpacity`) |
|
|
46
|
-
| `modalBackdropOnclick` | `DISMISS` → tap backdrop ends flow |
|
|
47
|
-
| Step `backdrop` / `backdropPadding` / `tooltipTargetBlocked` | Tooltip cutout hole, dim, and interaction blocking (bands block by default; hole sealed when `tooltipTargetBlocked`) |
|
|
48
|
-
|
|
49
|
-
## Tooltip target highlight
|
|
50
|
-
|
|
51
|
-
Source: `src/surfaces/resolveHighlightChrome.ts` + `src/surfaces/tooltip/TooltipHighlight.tsx`.
|
|
52
|
-
|
|
53
|
-
Web paints the ring as one CSS `box-shadow` on `.userflowjs-tooltip-backdrop`.
|
|
54
|
-
RN uses a **2px brand stroke + one translucent halo**, plus iOS `shadow*` /
|
|
55
|
-
Android `boxShadow` for blur. Theme spread is a placement gap only — it is
|
|
56
|
-
not painted as a thick solid bar.
|
|
57
|
-
|
|
58
|
-
| Web / theme token | RN usage |
|
|
59
|
-
| -------------------------- | -------------------------------------------------------------------------------- |
|
|
60
|
-
| `backdropHighlightColor` | Stroke color. Near-white (web default `#ffffff`) is treated as unset — see below |
|
|
61
|
-
| `backdropHighlightOpacity` | Caps the halo at `0.32` so it stays a wash |
|
|
62
|
-
| `backdropHighlightRadius` | Halo thickness (default `4`) |
|
|
63
|
-
| `backdropHighlightSpread` | Added to placement pad when type is `OUTSIDE` (default `0`). Not stroke width |
|
|
64
|
-
| `backdropHighlightType` | `OUTSIDE` (default) draws outside the hole; `INSIDE` insets |
|
|
65
|
-
|
|
66
|
-
Placement pad for `OUTSIDE` is `spread + strokeWidth + radius` so the tooltip
|
|
67
|
-
sits clear of the painted ring, not only the theme spread token.
|
|
68
|
-
| Corner radius | `8` + `backdropPadding` (web copies the target's computed radius; RN measure cannot) |
|
|
69
|
-
|
|
70
|
-
Color fallback: non-white `backdropHighlightColor` → `widgetBackgroundColor` →
|
|
71
|
-
`brandBackgroundColor` → `defaultButtonBackgroundColor` → `#1a57e6`. Web's
|
|
72
|
-
`#ffffff` default is skipped so the dimmed cutout does not get a white frame.
|
|
73
|
-
|
|
74
|
-
## Typography
|
|
75
|
-
|
|
76
|
-
| Theme token | RN usage |
|
|
77
|
-
| ------------ | ----------------------------------- |
|
|
78
|
-
| `fontColor` | Body / heading / close affordance |
|
|
79
|
-
| `fontSize` | Base body size |
|
|
80
|
-
| `fontFamily` | Applied where Text styles accept it |
|
|
81
|
-
|
|
82
|
-
## Quote / code-block — TreeDoc chrome
|
|
83
|
-
|
|
84
|
-
Source of truth: web `userflowjs/styles/bubble-frame.styl` in the main Userflow
|
|
85
|
-
web repo (blockquote / `code` / `pre > code`) and TreeDoc markup
|
|
86
|
-
(`blockquote`, `pre > code`). Spacing tokens from web `$padding-*` assume
|
|
87
|
-
**1rem = 16px**.
|
|
88
|
-
|
|
89
|
-
Implementation: `src/content/RichContentRenderer.tsx` + `src/content/themeColor.ts`.
|
|
90
|
-
|
|
91
|
-
| Token | rem | RN px |
|
|
92
|
-
| ------------- | ----- | ----- |
|
|
93
|
-
| `$padding-2` | 0.125 | 2 |
|
|
94
|
-
| `$padding-4` | 0.25 | 4 |
|
|
95
|
-
| `$padding-8` | 0.5 | 8 |
|
|
96
|
-
| `$padding-16` | 1 | 16 |
|
|
97
|
-
|
|
98
|
-
Root content uses `gap: 8` (`ROOT_CONTENT_GAP`). Quote/code add
|
|
99
|
-
`marginBottom: 8` when not last so gap + margin ≈ web’s 16px block spacing.
|
|
100
|
-
|
|
101
|
-
### Quote (`blockquote`)
|
|
102
|
-
|
|
103
|
-
| Web rule | RN |
|
|
104
|
-
| ----------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
105
|
-
| `border-inline-start: 2px solid rgba(fg, 0.15)` | `borderStartWidth: 2`, `borderStartColor` via `foregroundAlpha(fontColor, 0.15)` |
|
|
106
|
-
| `padding: $padding-4 $padding-16` | `paddingVertical: 4`, `paddingHorizontal: 16` |
|
|
107
|
-
| `margin-bottom: $padding-16` when not last | `marginBottom: 8` (+ root gap 8); last painted sibling: 0 |
|
|
108
|
-
| `min-height: var(--userflow-line-height, 24px)` | `minHeight: 24` |
|
|
109
|
-
| Not italic by default | No forced `fontStyle: 'italic'` |
|
|
110
|
-
|
|
111
|
-
### Code-block (`pre > code`) and inline `code` mark
|
|
112
|
-
|
|
113
|
-
| Web rule | RN |
|
|
114
|
-
| -------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
115
|
-
| Mono stack (`SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace`) | iOS `Menlo`, Android `monospace` |
|
|
116
|
-
| `font-size: calc(font-size * 0.875)` | `fontSize * 0.875` |
|
|
117
|
-
| `background: rgba(fg, 0.05)` | `foregroundAlpha(fontColor, 0.05)` (hex / rgb / rgba) |
|
|
118
|
-
| `border-radius: calc(font-size * 0.25)` | `fontSize * 0.25` |
|
|
119
|
-
| `pre > code` padding `$padding-8` | `padding: 8` + horizontal `ScrollView` (`nestedScrollEnabled`) |
|
|
120
|
-
| Inline `code` padding `$padding-2 $padding-4` | View chrome wrapper + `paddingVertical: 2`, `paddingHorizontal: 4` |
|
|
121
|
-
|
|
122
|
-
## Safe area
|
|
123
|
-
|
|
124
|
-
Surfaces pad with `useSurfaceLayoutInsets`: optional-peer
|
|
125
|
-
`react-native-safe-area-context` (zero fallback) plus a 24px screen margin so
|
|
126
|
-
cards clear Dynamic Island / home indicator regions. While Builder preview is
|
|
127
|
-
active, the measured Preview banner height is added to the top inset so the
|
|
128
|
-
close control is not trapped under the banner. Tall cards cap at that padded
|
|
129
|
-
window height and scroll internally (`overflow: 'hidden'` on the fill; the
|
|
130
|
-
shadow/notch shell stays `overflow: 'visible'`).
|