@getuserfeedback/react 0.6.3 → 0.6.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.
- package/README.md +21 -582
- package/dist/index.js +2 -2
- package/dist/index.js.map +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# @getuserfeedback/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
In-product surveys for React apps, from [getuserfeedback.com](https://getuserfeedback.com). Ask one short question at the right moment, and read every answer.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
> Using plain JavaScript or another framework? Check out [@getuserfeedback/sdk](https://www.npmjs.com/package/@getuserfeedback/sdk).
|
|
5
|
+
> Plain JavaScript or another framework? Use [@getuserfeedback/sdk](https://www.npmjs.com/package/@getuserfeedback/sdk).
|
|
8
6
|
|
|
9
7
|
## Install
|
|
10
8
|
|
|
@@ -14,105 +12,13 @@ npm install @getuserfeedback/react
|
|
|
14
12
|
|
|
15
13
|
Requires `react >= 18`.
|
|
16
14
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
### 1. Get an API key
|
|
20
|
-
Sign up on [getuserfeedback.com](https://getuserfeedback.com) and grab your key in Settings.
|
|
21
|
-
|
|
22
|
-
### 2. Add the provider
|
|
23
|
-
|
|
24
|
-
```tsx
|
|
25
|
-
import { GetUserFeedbackProvider } from "@getuserfeedback/react";
|
|
26
|
-
|
|
27
|
-
export function App() {
|
|
28
|
-
return (
|
|
29
|
-
<GetUserFeedbackProvider clientOptions={{ apiKey: "YOUR_API_KEY" }}>
|
|
30
|
-
<AppRoutes />
|
|
31
|
-
</GetUserFeedbackProvider>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### 3. Start collecting responses
|
|
37
|
-
|
|
38
|
-
Pick one of our curated templates on [getuserfeedback.com/templates](https://getuserfeedback.com/templates) — from a post-signup welcome message to customer satisfaction surveys, feedback forms, and more. You can also customize everything or start from scratch.
|
|
39
|
-
|
|
40
|
-
### 4. Get feedback — improve product — repeat
|
|
41
|
-
Our widgets are optimized to get you *more* responses. Polished UI, crafted copy, templates, and a million subtle tricks. Result — you get more responses, make better roadmap calls, and find product-market fit faster.
|
|
42
|
-
|
|
43
|
-
### Bonus
|
|
44
|
-
Our AI-powered theme editor allows a complete overhaul of the widget look and feel with a single prompt. Demo: [getuserfeedback.com/themes](https://getuserfeedback.com/themes).
|
|
45
|
-
|
|
46
|
-
## Advanced use cases
|
|
47
|
-
### Feedback form
|
|
48
|
-
You can open Widget surveys from code, e.g., show a feedback form when the user clicks a dedicated button in your UI.
|
|
49
|
-
|
|
50
|
-
```tsx
|
|
51
|
-
import { useFlow } from "@getuserfeedback/react";
|
|
52
|
-
|
|
53
|
-
const CONTACT_SUPPORT_FLOW_ID = "YOUR_FLOW_ID";
|
|
54
|
-
|
|
55
|
-
export function ContactSupportMenuItem() {
|
|
56
|
-
const { open } = useFlow({ flowId: CONTACT_SUPPORT_FLOW_ID });
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<button type="button" onClick={() => open()}>
|
|
60
|
-
Contact support
|
|
61
|
-
</button>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Bring your own UI
|
|
67
|
-
By default, flows are displayed in a floating container and [themed](https://getuserfeedback.com/themes) accordingly. Beyond that, you can replace the default floating container with your own. This gives you unprecedented customization options for a seamless user experience.
|
|
15
|
+
## For AI agents
|
|
68
16
|
|
|
69
|
-
|
|
17
|
+
Installing this for someone? Start with our MCP server, not with this page. Connect `https://mcp.getuserfeedback.com` (listed in the MCP registry as `com.getuserfeedback/mcp`) and read its get-started resource — `getuserfeedback://docs/get-started`. It walks the whole path in order: workspace, API key (the MCP hands it to you), this package's install, the first survey, and which steps need a human.
|
|
70
18
|
|
|
71
|
-
|
|
72
|
-
import { useFlow } from "@getuserfeedback/react";
|
|
73
|
-
import { Button, Dialog, DialogContent } from "ui";
|
|
74
|
-
|
|
75
|
-
export function FeedbackDialog() {
|
|
76
|
-
const {
|
|
77
|
-
isLoading,
|
|
78
|
-
setOpen,
|
|
79
|
-
shouldRenderContainer,
|
|
80
|
-
containerRef,
|
|
81
|
-
width,
|
|
82
|
-
height
|
|
83
|
-
} = useFlow({ flowId: "YOUR_FLOW_ID", container: "custom" });
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<>
|
|
87
|
-
<Button
|
|
88
|
-
type="button"
|
|
89
|
-
onClick={() => setOpen(true)}
|
|
90
|
-
disabled={isLoading}
|
|
91
|
-
>
|
|
92
|
-
Open feedback
|
|
93
|
-
</Button>
|
|
94
|
-
<Dialog open={shouldRenderContainer} onOpenChange={setOpen}>
|
|
95
|
-
<DialogContent
|
|
96
|
-
ref={containerRef}
|
|
97
|
-
style={{ width, height }}
|
|
98
|
-
/>
|
|
99
|
-
</Dialog>
|
|
100
|
-
</>
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## API Overview
|
|
106
|
-
|
|
107
|
-
- `GetUserFeedbackProvider`
|
|
108
|
-
- `useGetUserFeedback()`
|
|
109
|
-
- `useFeedback()`
|
|
110
|
-
- `useFlow({ flowId, prefetchOnMount?, hideCloseButton?, container?: "default" | "custom" })`
|
|
111
|
-
- `useDefaultFlowContainer()`
|
|
112
|
-
|
|
113
|
-
## `GetUserFeedbackProvider`
|
|
19
|
+
## Get started
|
|
114
20
|
|
|
115
|
-
|
|
21
|
+
Grab your Public API key from the dashboard — Settings → Widget — and mount the provider near the root of your app:
|
|
116
22
|
|
|
117
23
|
```tsx
|
|
118
24
|
import { GetUserFeedbackProvider } from "@getuserfeedback/react";
|
|
@@ -120,505 +26,38 @@ import { GetUserFeedbackProvider } from "@getuserfeedback/react";
|
|
|
120
26
|
export function App() {
|
|
121
27
|
return (
|
|
122
28
|
<GetUserFeedbackProvider clientOptions={{ apiKey: "YOUR_API_KEY" }}>
|
|
123
|
-
<
|
|
124
|
-
</GetUserFeedbackProvider>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Options
|
|
130
|
-
|
|
131
|
-
Pass SDK options via `clientOptions`:
|
|
132
|
-
|
|
133
|
-
- `apiKey` (`string`, required): your project API key.
|
|
134
|
-
- `colorScheme` (`"light" | "dark" | "system" | { autoDetectColorScheme: string[] }`, optional):
|
|
135
|
-
controls widget theme mode. See [Dark mode](#dark-mode).
|
|
136
|
-
- `disableAutoLoad` (`boolean`, optional): if `true`, call `client.load()` manually.
|
|
137
|
-
- `disableTelemetry` (`boolean`, optional): disables anonymous telemetry used for performance and error monitoring.
|
|
138
|
-
|
|
139
|
-
### Delay widget load
|
|
140
|
-
|
|
141
|
-
If you want to manually control when the widget starts loading:
|
|
142
|
-
|
|
143
|
-
```tsx
|
|
144
|
-
import { useEffect } from "react";
|
|
145
|
-
import {
|
|
146
|
-
GetUserFeedbackProvider,
|
|
147
|
-
useGetUserFeedback,
|
|
148
|
-
} from "@getuserfeedback/react";
|
|
149
|
-
|
|
150
|
-
function LoadWidgetAfterConsent({
|
|
151
|
-
hasResolvedConsent,
|
|
152
|
-
}: {
|
|
153
|
-
hasResolvedConsent: boolean;
|
|
154
|
-
}) {
|
|
155
|
-
const client = useGetUserFeedback();
|
|
156
|
-
|
|
157
|
-
useEffect(() => {
|
|
158
|
-
if (!hasResolvedConsent) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
client.load();
|
|
163
|
-
}, [client, hasResolvedConsent]);
|
|
164
|
-
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function App({ hasResolvedConsent }: { hasResolvedConsent: boolean }) {
|
|
169
|
-
return (
|
|
170
|
-
<GetUserFeedbackProvider
|
|
171
|
-
clientOptions={{ apiKey: "YOUR_API_KEY", disableAutoLoad: true }}
|
|
172
|
-
>
|
|
173
|
-
<AppRoutes />
|
|
174
|
-
<LoadWidgetAfterConsent hasResolvedConsent={hasResolvedConsent} />
|
|
175
|
-
</GetUserFeedbackProvider>
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## Submit feedback
|
|
181
|
-
|
|
182
|
-
Use `useFeedback()` when your app renders the feedback controls. The complete
|
|
183
|
-
feedback payload is provided when the user submits it.
|
|
184
|
-
|
|
185
|
-
```tsx
|
|
186
|
-
import { useFeedback } from "@getuserfeedback/react";
|
|
187
|
-
|
|
188
|
-
export function HelpfulButton({
|
|
189
|
-
onError,
|
|
190
|
-
}: {
|
|
191
|
-
onError: (error: unknown) => void;
|
|
192
|
-
}) {
|
|
193
|
-
const { submit, isSubmitting } = useFeedback();
|
|
194
|
-
|
|
195
|
-
async function markHelpful() {
|
|
196
|
-
try {
|
|
197
|
-
await submit({
|
|
198
|
-
target: {
|
|
199
|
-
collection: "docs-page",
|
|
200
|
-
id: "getting-started",
|
|
201
|
-
},
|
|
202
|
-
category: "helpfulness",
|
|
203
|
-
value: true,
|
|
204
|
-
});
|
|
205
|
-
} catch (error) {
|
|
206
|
-
onError(error);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return (
|
|
211
|
-
<button
|
|
212
|
-
type="button"
|
|
213
|
-
disabled={isSubmitting}
|
|
214
|
-
onClick={markHelpful}
|
|
215
|
-
>
|
|
216
|
-
Helpful
|
|
217
|
-
</button>
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
`isSubmitting` remains `true` while submissions from that hook are pending.
|
|
223
|
-
`submit()` resolves with the accepted evaluation receipt and rejects if capture
|
|
224
|
-
fails.
|
|
225
|
-
|
|
226
|
-
### Read previous feedback
|
|
227
|
-
|
|
228
|
-
Call `fetch()` only in contexts that need to restore feedback previously given
|
|
229
|
-
by the current user. It does not run automatically. Feedback readback requires
|
|
230
|
-
`auth.jwt`; calling `identify()` alone does not authorize it.
|
|
231
|
-
|
|
232
|
-
```tsx
|
|
233
|
-
const { fetch, isFetching } = useFeedback();
|
|
234
|
-
|
|
235
|
-
const { items } = await fetch({
|
|
236
|
-
selectors: [
|
|
237
|
-
{
|
|
238
|
-
target: { collection: "docs-page", id: "getting-started" },
|
|
239
|
-
category: "helpfulness",
|
|
240
|
-
},
|
|
241
|
-
],
|
|
242
|
-
});
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
`isFetching` remains `true` while fetches from that hook are pending. Results
|
|
246
|
-
stay aligned with selector order, with `evaluation: null` when no match exists.
|
|
247
|
-
|
|
248
|
-
## Identify users
|
|
249
|
-
|
|
250
|
-
Use identity to unlock cohort targeting and attach responses to user profiles.
|
|
251
|
-
|
|
252
|
-
### `client.identify(userId, traits?)` or `client.identify(traits)`
|
|
253
|
-
|
|
254
|
-
```tsx
|
|
255
|
-
import { useEffect } from "react";
|
|
256
|
-
import { useGetUserFeedback } from "@getuserfeedback/react";
|
|
257
|
-
|
|
258
|
-
type User = {
|
|
259
|
-
id: string;
|
|
260
|
-
email?: string;
|
|
261
|
-
firstName?: string;
|
|
262
|
-
lastName?: string;
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
export function Identify({ user }: { user: User | null }) {
|
|
266
|
-
const client = useGetUserFeedback();
|
|
267
|
-
|
|
268
|
-
useEffect(() => {
|
|
269
|
-
if (!user) {
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
client.identify(user.id, {
|
|
274
|
-
email: user.email,
|
|
275
|
-
firstName: user.firstName,
|
|
276
|
-
lastName: user.lastName,
|
|
277
|
-
});
|
|
278
|
-
}, [client, user]);
|
|
279
|
-
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### `client.reset()`
|
|
285
|
-
|
|
286
|
-
Call this on logout to clear active identity state and auth (if you use auth):
|
|
287
|
-
|
|
288
|
-
```tsx
|
|
289
|
-
import { useGetUserFeedback } from "@getuserfeedback/react";
|
|
290
|
-
import { useAuth } from "./auth";
|
|
291
|
-
|
|
292
|
-
export function LogoutButton() {
|
|
293
|
-
const { signOut } = useAuth();
|
|
294
|
-
const { reset } = useGetUserFeedback();
|
|
295
|
-
|
|
296
|
-
const handleLogout = async () => {
|
|
297
|
-
await signOut();
|
|
298
|
-
await reset();
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
return (
|
|
302
|
-
<button type="button" onClick={handleLogout}>
|
|
303
|
-
Log out
|
|
304
|
-
</button>
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
## Dark mode
|
|
310
|
-
|
|
311
|
-
If you're using industry-standard ways to indicate when dark mode is *on*, the widget will pick it up and automatically keep itself in sync. If you only have light mode, the widget will stay in light mode.
|
|
312
|
-
|
|
313
|
-
Default behavior:
|
|
314
|
-
- The widget observes `class` and `data-theme` attributes on both `html` and `body`.
|
|
315
|
-
- `class="dark"` / `data-theme="dark"` enables dark mode.
|
|
316
|
-
- `class="light"` / `data-theme="light"` enables light mode.
|
|
317
|
-
- `class="system"` / `data-theme="system"` follows the user's system preference (`prefers-color-scheme`).
|
|
318
|
-
- If nothing is detected, it falls back to `"light"`.
|
|
319
|
-
|
|
320
|
-
You can control the behavior using the `colorScheme` option when creating a client, and you can also update it later.
|
|
321
|
-
|
|
322
|
-
### Set a static color scheme at startup
|
|
323
|
-
|
|
324
|
-
```tsx
|
|
325
|
-
import { GetUserFeedbackProvider } from "@getuserfeedback/react";
|
|
326
|
-
|
|
327
|
-
export function App() {
|
|
328
|
-
return (
|
|
329
|
-
<GetUserFeedbackProvider
|
|
330
|
-
clientOptions={{
|
|
331
|
-
apiKey: "YOUR_API_KEY",
|
|
332
|
-
colorScheme: "dark", // set to a static value
|
|
333
|
-
}}
|
|
334
|
-
>
|
|
335
|
-
<AppRoutes />
|
|
336
|
-
</GetUserFeedbackProvider>
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
`colorScheme` supports:
|
|
342
|
-
- `{ autoDetectColorScheme: string[] }`
|
|
343
|
-
- `"light" | "dark" | "system"`
|
|
344
|
-
|
|
345
|
-
### Update color scheme at runtime
|
|
346
|
-
|
|
347
|
-
```tsx
|
|
348
|
-
import { useEffect } from "react";
|
|
349
|
-
import { useGetUserFeedback } from "@getuserfeedback/react";
|
|
350
|
-
|
|
351
|
-
type ThemePreference = "light" | "dark" | "system";
|
|
352
|
-
|
|
353
|
-
export function SyncWidgetTheme({
|
|
354
|
-
themePreference,
|
|
355
|
-
}: {
|
|
356
|
-
themePreference: ThemePreference;
|
|
357
|
-
}) {
|
|
358
|
-
const client = useGetUserFeedback();
|
|
359
|
-
|
|
360
|
-
useEffect(() => {
|
|
361
|
-
client.configure({ colorScheme: themePreference });
|
|
362
|
-
}, [client, themePreference]);
|
|
363
|
-
|
|
364
|
-
return null;
|
|
365
|
-
}
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
You can also switch runtime behavior back to host-driven auto-detection:
|
|
369
|
-
|
|
370
|
-
```tsx
|
|
371
|
-
import { useEffect } from "react";
|
|
372
|
-
import { useGetUserFeedback } from "@getuserfeedback/react";
|
|
373
|
-
|
|
374
|
-
export function SyncWidgetToAppTheme({
|
|
375
|
-
useAppTheme,
|
|
376
|
-
}: {
|
|
377
|
-
useAppTheme: boolean;
|
|
378
|
-
}) {
|
|
379
|
-
const client = useGetUserFeedback();
|
|
380
|
-
|
|
381
|
-
useEffect(() => {
|
|
382
|
-
if (!useAppTheme) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
client.configure({
|
|
387
|
-
colorScheme: {
|
|
388
|
-
autoDetectColorScheme: ["class", "data-theme"], // default
|
|
389
|
-
},
|
|
390
|
-
});
|
|
391
|
-
}, [client, useAppTheme]);
|
|
392
|
-
|
|
393
|
-
return null;
|
|
394
|
-
}
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
## Auth (optional)
|
|
398
|
-
|
|
399
|
-
You can run the SDK without auth. This is standard industry practice for client-side widgets like ours, so it is not enforced by default. However, we encourage you to implement auth for stricter controls.
|
|
400
|
-
|
|
401
|
-
Enable JWT validation in your workspace settings and pass a signed token from your app.
|
|
402
|
-
|
|
403
|
-
### Clerk example: set, refresh, and clear auth token
|
|
404
|
-
|
|
405
|
-
```tsx
|
|
406
|
-
import { useEffect } from "react";
|
|
407
|
-
import { useAuth } from "@clerk/clerk-react";
|
|
408
|
-
import { useGetUserFeedback } from "@getuserfeedback/react";
|
|
409
|
-
|
|
410
|
-
const AUTH_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
411
|
-
|
|
412
|
-
// Mount once inside both <ClerkProvider> and <GetUserFeedbackProvider>.
|
|
413
|
-
export function GetUserFeedbackClerkAuth() {
|
|
414
|
-
const client = useGetUserFeedback();
|
|
415
|
-
const { isLoaded, userId, sessionId, getToken } = useAuth();
|
|
416
|
-
|
|
417
|
-
useEffect(() => {
|
|
418
|
-
if (!isLoaded) {
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
let isDisposed = false;
|
|
423
|
-
|
|
424
|
-
const syncAuthToken = async () => {
|
|
425
|
-
// Signed out: clear auth token.
|
|
426
|
-
if (!userId) {
|
|
427
|
-
await client.configure({ auth: { jwt: null } });
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Signed in: fetch/refresh Clerk JWT and pass it to the SDK.
|
|
432
|
-
const token = await getToken({ template: "getuserfeedback" });
|
|
433
|
-
if (isDisposed) {
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (!token) {
|
|
438
|
-
await client.configure({ auth: { jwt: null } });
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
await client.configure({ auth: { jwt: { token } } });
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
syncAuthToken();
|
|
446
|
-
const refreshTimer = window.setInterval(() => {
|
|
447
|
-
syncAuthToken();
|
|
448
|
-
}, AUTH_REFRESH_INTERVAL_MS);
|
|
449
|
-
|
|
450
|
-
return () => {
|
|
451
|
-
isDisposed = true;
|
|
452
|
-
window.clearInterval(refreshTimer);
|
|
453
|
-
};
|
|
454
|
-
}, [client, getToken, isLoaded, sessionId, userId]);
|
|
455
|
-
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
## Privacy and consent (GDPR, CCPA, etc)
|
|
461
|
-
|
|
462
|
-
Consent settings control non-essential data scopes: analytics, storage, personalization, ads.
|
|
463
|
-
|
|
464
|
-
By default, the widget starts with `granted` consent. For compliance with privacy regulations like GDPR and CCPA, you may want to choose a different default.
|
|
465
|
-
|
|
466
|
-
### Set default consent to `pending`
|
|
467
|
-
```tsx
|
|
468
|
-
import { GetUserFeedbackProvider } from "@getuserfeedback/react";
|
|
469
|
-
|
|
470
|
-
export function App() {
|
|
471
|
-
return (
|
|
472
|
-
<GetUserFeedbackProvider
|
|
473
|
-
clientOptions={{
|
|
474
|
-
apiKey: "YOUR_API_KEY",
|
|
475
|
-
defaultConsent: "pending", // set to `pending`
|
|
476
|
-
}}
|
|
477
|
-
>
|
|
478
|
-
<AppRoutes />
|
|
29
|
+
<YourApp />
|
|
479
30
|
</GetUserFeedbackProvider>
|
|
480
31
|
);
|
|
481
32
|
}
|
|
482
33
|
```
|
|
483
|
-
With `pending` consent set by default, all non-essential data scopes are considered effectively denied.
|
|
484
34
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
For example, sync consent from your cookie banner or CMP state:
|
|
35
|
+
If your app has signed-in users, also identify them. It lets surveys target signed-in users, and each answer arrives attributed to the person instead of anonymous. Call identify from an effect keyed on your auth state — resetting only when a signed-in user signs out, so anonymous visitors keep their identity — and render the component anywhere inside the provider:
|
|
488
36
|
|
|
489
37
|
```tsx
|
|
490
|
-
import { useEffect } from "react";
|
|
38
|
+
import { useEffect, useRef } from "react";
|
|
491
39
|
import { useGetUserFeedback } from "@getuserfeedback/react";
|
|
492
|
-
import { useCookieConsent } from "./cookie-consent";
|
|
493
40
|
|
|
494
|
-
|
|
41
|
+
function IdentifyUser({ user }: { user: { id: string; email: string } | null }) {
|
|
495
42
|
const client = useGetUserFeedback();
|
|
496
|
-
const
|
|
497
|
-
|
|
43
|
+
const wasIdentified = useRef(false);
|
|
498
44
|
useEffect(() => {
|
|
499
|
-
if (
|
|
500
|
-
client.
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
client.configure({ consent: "denied" });
|
|
506
|
-
return;
|
|
45
|
+
if (user) {
|
|
46
|
+
client.identify(user.id, { email: user.email });
|
|
47
|
+
wasIdentified.current = true;
|
|
48
|
+
} else if (wasIdentified.current) {
|
|
49
|
+
wasIdentified.current = false;
|
|
50
|
+
client.reset();
|
|
507
51
|
}
|
|
508
|
-
|
|
509
|
-
client.configure({
|
|
510
|
-
consent: ["analytics.measurement", "analytics.storage"],
|
|
511
|
-
});
|
|
512
|
-
}, [analyticsEnabled, client, hasAnswered]);
|
|
513
|
-
|
|
52
|
+
}, [user, client]);
|
|
514
53
|
return null;
|
|
515
54
|
}
|
|
516
55
|
```
|
|
517
56
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
```tsx
|
|
521
|
-
import { useGetUserFeedback } from "@getuserfeedback/react";
|
|
522
|
-
|
|
523
|
-
type PrivacySettings = {
|
|
524
|
-
analyticsEnabled: boolean;
|
|
525
|
-
personalizedAdsEnabled: boolean;
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
export function useSavePrivacySettings() {
|
|
529
|
-
const client = useGetUserFeedback();
|
|
530
|
-
|
|
531
|
-
return async ({
|
|
532
|
-
analyticsEnabled,
|
|
533
|
-
personalizedAdsEnabled,
|
|
534
|
-
}: PrivacySettings) => {
|
|
535
|
-
const grantedScopes: Array<
|
|
536
|
-
| "analytics.measurement"
|
|
537
|
-
| "analytics.storage"
|
|
538
|
-
| "ads.storage"
|
|
539
|
-
| "ads.user_data"
|
|
540
|
-
| "ads.personalization"
|
|
541
|
-
> = [];
|
|
542
|
-
|
|
543
|
-
if (analyticsEnabled) {
|
|
544
|
-
grantedScopes.push("analytics.measurement", "analytics.storage");
|
|
545
|
-
}
|
|
57
|
+
That's it. The widget is running, and surveys you publish in the dashboard show up based on the targeting you set there — nothing shows until you publish something.
|
|
546
58
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
"ads.storage",
|
|
550
|
-
"ads.user_data",
|
|
551
|
-
"ads.personalization",
|
|
552
|
-
);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
await client.configure({
|
|
556
|
-
consent: grantedScopes.length > 0 ? grantedScopes : "denied",
|
|
557
|
-
});
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
```
|
|
562
|
-
`defaultConsent` supports:
|
|
563
|
-
- `"pending" | "granted" (default) | "denied" | "revoked"`
|
|
564
|
-
- or a granted-scope list like `["analytics.measurement", "analytics.storage"]`
|
|
565
|
-
|
|
566
|
-
### Impact of different consent settings
|
|
567
|
-
|
|
568
|
-
- Response collection: never affected.
|
|
569
|
-
- Opening Widget surveys: never affected.
|
|
570
|
-
- Cohort targeting: rules that depend on persisted activity, identity continuity, or storage-backed signals require both `analytics.measurement` and `analytics.storage`.
|
|
571
|
-
- Context targeting: rules based on current-page context (URL, referrer, timing, host signals) require `analytics.measurement` only.
|
|
572
|
-
|
|
573
|
-
## Advanced — open Widget surveys from code
|
|
574
|
-
|
|
575
|
-
### Open a survey
|
|
576
|
-
|
|
577
|
-
```tsx
|
|
578
|
-
import { useFlow } from "@getuserfeedback/react";
|
|
579
|
-
|
|
580
|
-
const BUG_REPORT_FLOW_ID = "YOUR_FLOW_ID";
|
|
581
|
-
|
|
582
|
-
export function ReportBugButton() {
|
|
583
|
-
const { open } = useFlow({ flowId: BUG_REPORT_FLOW_ID });
|
|
584
|
-
|
|
585
|
-
return (
|
|
586
|
-
<button type="button" onClick={() => open()}>
|
|
587
|
-
Report a bug
|
|
588
|
-
</button>
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
### Prefetch and prerender
|
|
594
|
-
|
|
595
|
-
Prefetching loads flow resources over the network. Prerendering warms up the UI
|
|
596
|
-
before opening. Opening still handles the render step if you have not prerendered
|
|
597
|
-
first.
|
|
598
|
-
|
|
599
|
-
```tsx
|
|
600
|
-
import { useFlow } from "@getuserfeedback/react";
|
|
601
|
-
|
|
602
|
-
const FEEDBACK_FLOW_ID = "YOUR_FLOW_ID";
|
|
603
|
-
|
|
604
|
-
export function HelpMenuFeedbackItem() {
|
|
605
|
-
const { prerender, open } = useFlow({
|
|
606
|
-
flowId: FEEDBACK_FLOW_ID,
|
|
607
|
-
prefetchOnMount: true,
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
return (
|
|
611
|
-
<button
|
|
612
|
-
type="button"
|
|
613
|
-
onMouseEnter={() => prerender()}
|
|
614
|
-
onFocus={() => prerender()}
|
|
615
|
-
onClick={() => open()}
|
|
616
|
-
>
|
|
617
|
-
Share product feedback
|
|
618
|
-
</button>
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
```
|
|
59
|
+
- Full guide: [getuserfeedback.com/docs/get-started/react-sdk](https://getuserfeedback.com/docs/get-started/react-sdk)
|
|
60
|
+
- API reference: [getuserfeedback.com/docs/reference/react-sdk-reference](https://getuserfeedback.com/docs/reference/react-sdk-reference)
|
|
622
61
|
|
|
623
62
|
## Links
|
|
624
63
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{createClient as $}from"@getuserfeedback/sdk";import{createContext as j,useCallback as K,useContext as z,useEffect as L,useMemo as F,useRef as q,useState as H,useSyncExternalStore as k}from"react";import{jsx as g}from"react/jsx-runtime";var J={isOpen:!1,isLoading:!1,shouldRenderContainer:!1,width:void 0,height:void 0},p=(U)=>({isOpen:U.isOpen,isLoading:U.isLoading,width:U.width,height:U.height}),r=(U,O)=>U.isOpen===O.isOpen&&U.isLoading===O.isLoading&&U.width===O.width&&U.height===O.height,D={isOpen:!1,isLoading:!1,width:void 0,height:void 0},P=(U)=>typeof U.width==="number"&&typeof U.height==="number";function V(U){let O=q(U.getFlowState());return K(()=>{let G=U.getFlowState(),R=O.current;if(r(R,G))return R;return O.current=G,G},[U])}function M(U,O,G){let R=V(U),I=q(null),A=q(null),v=K(()=>{let E=R();if(I.current===E){let N=A.current;if(N!==null)return N}let _=O(E);return I.current=E,A.current=_,_},[R,O]);return k((E)=>U.subscribeFlowState(()=>{E()},{emitInitial:!1}),v,G)}var y=j(null);function w({children:U,clientOptions:O}){let G=$(O),R=q(null),I=q(0),A=M(G,p,()=>J),v=K((b)=>{if(R.current=b,I.current===0)return;G.setDefaultContainerPolicy({kind:"hostContainer",host:b,sharing:"perFlowRun"})},[G]),E=K(()=>{if(I.current+=1,I.current===1)G.setDefaultContainerPolicy({kind:"hostContainer",host:R.current,sharing:"perFlowRun"});return()=>{if(I.current=Math.max(0,I.current-1),I.current===0)G.setDefaultContainerPolicy({kind:"floating"})}},[G]),_=F(()=>{if(!A.isOpen&&!A.isLoading)return J;return{isOpen:A.isOpen,isLoading:A.isLoading,shouldRenderContainer:!0,width:A.width,height:A.height}},[A]),N=F(()=>({client:G,apiKey:O.apiKey,setDefaultFlowContainer:v,registerDefaultFlowContainerConsumer:E,defaultFlowContainerState:_}),[G,O.apiKey,_,E,v]);return g(y.Provider,{value:N,children:U})}var h=(U)=>{let O=z(y);if(!O)throw Error("useFlow must be used within a GetUserFeedbackProvider");let{client:G}=O,{flowId:R,prefetchOnMount:I=!1,hideCloseButton:A}=U.flow,v=F(()=>G.flow(R),[G,R]),E=U.mode==="container",_=q(null),N=M(v,(T)=>T,()=>D),b=K((T)=>!E?v.open({hideCloseButton:A,metadata:T?.metadata}):v.open({container:_.current??null,hideCloseButton:A,metadata:T?.metadata}),[v,A,E]),B=K(()=>v.close(),[v]),W=K((T)=>T?b():B(),[B,b]),X=K((T)=>{if(!E)return;_.current=T,v.setContainer(T)},[v,E]),Y=P(N),Z=E&&(N.isOpen||N.isLoading&&Y);return L(()=>{if(!I)return;v.prefetch()},[v,I]),{isOpen:N.isOpen,isLoading:N.isLoading,width:N.width,height:N.height,shouldRenderContainer:Z,setOpen:W,open:b,close:B,prerender:()=>v.prerender({hideCloseButton:A}),containerRef:X}};function m(U){let G=(U.container??"default")==="custom"?"container":"default",R=h({flow:{flowId:U.flowId,prefetchOnMount:U.prefetchOnMount,hideCloseButton:U.hideCloseButton},mode:G});if(G==="container")return{container:"custom",isOpen:R.isOpen,isLoading:R.isLoading,width:R.width,height:R.height,shouldRenderContainer:R.shouldRenderContainer,open:R.open,setOpen:R.setOpen,prerender:R.prerender,containerRef:R.containerRef};return{container:"default",isOpen:R.isOpen,isLoading:R.isLoading,width:R.width,height:R.height,open:R.open,close:R.close,prerender:R.prerender}}function d(){let U=z(y);if(!U)throw Error("useDefaultFlowContainer must be used within a GetUserFeedbackProvider");let{client:O,defaultFlowContainerState:G,registerDefaultFlowContainerConsumer:R,setDefaultFlowContainer:I}=U;L(()=>R(),[R]);let A=K((_)=>{I(_)},[I]),v=K(()=>O.close(),[O]),E=K((_)=>{if(_)return Promise.resolve();return v()},[v]);return{isOpen:G.isOpen,isLoading:G.isLoading,shouldRenderContainer:G.shouldRenderContainer,width:G.width,height:G.height,setOpen:E,containerRef:A}}function x(){let U=z(y);if(!U)throw Error("useFeedback must be used within a GetUserFeedbackProvider");let[O,G]=H(0),[R,I]=H(0),A=K(async(E)=>{G((_)=>_+1);try{return await U.client.feedback.fetch(E)}finally{G((_)=>_-1)}},[U.client]),v=K(async(E)=>{I((_)=>_+1);try{return await U.client.feedback.submit(E)}finally{I((_)=>_-1)}},[U.client]);return{fetch:A,isFetching:O>0,isSubmitting:R>0,submit:v}}function f(){let U=z(y);if(!U)throw Error("useGetUserFeedback must be used within a GetUserFeedbackProvider");return U.client}var Q="0.6.
|
|
2
|
+
import{createClient as $}from"@getuserfeedback/sdk";import{createContext as j,useCallback as K,useContext as z,useEffect as L,useMemo as F,useRef as q,useState as H,useSyncExternalStore as k}from"react";import{jsx as g}from"react/jsx-runtime";var J={isOpen:!1,isLoading:!1,shouldRenderContainer:!1,width:void 0,height:void 0},p=(U)=>({isOpen:U.isOpen,isLoading:U.isLoading,width:U.width,height:U.height}),r=(U,O)=>U.isOpen===O.isOpen&&U.isLoading===O.isLoading&&U.width===O.width&&U.height===O.height,D={isOpen:!1,isLoading:!1,width:void 0,height:void 0},P=(U)=>typeof U.width==="number"&&typeof U.height==="number";function V(U){let O=q(U.getFlowState());return K(()=>{let G=U.getFlowState(),R=O.current;if(r(R,G))return R;return O.current=G,G},[U])}function M(U,O,G){let R=V(U),I=q(null),A=q(null),v=K(()=>{let E=R();if(I.current===E){let N=A.current;if(N!==null)return N}let _=O(E);return I.current=E,A.current=_,_},[R,O]);return k((E)=>U.subscribeFlowState(()=>{E()},{emitInitial:!1}),v,G)}var y=j(null);function w({children:U,clientOptions:O}){let G=$(O),R=q(null),I=q(0),A=M(G,p,()=>J),v=K((b)=>{if(R.current=b,I.current===0)return;G.setDefaultContainerPolicy({kind:"hostContainer",host:b,sharing:"perFlowRun"})},[G]),E=K(()=>{if(I.current+=1,I.current===1)G.setDefaultContainerPolicy({kind:"hostContainer",host:R.current,sharing:"perFlowRun"});return()=>{if(I.current=Math.max(0,I.current-1),I.current===0)G.setDefaultContainerPolicy({kind:"floating"})}},[G]),_=F(()=>{if(!A.isOpen&&!A.isLoading)return J;return{isOpen:A.isOpen,isLoading:A.isLoading,shouldRenderContainer:!0,width:A.width,height:A.height}},[A]),N=F(()=>({client:G,apiKey:O.apiKey,setDefaultFlowContainer:v,registerDefaultFlowContainerConsumer:E,defaultFlowContainerState:_}),[G,O.apiKey,_,E,v]);return g(y.Provider,{value:N,children:U})}var h=(U)=>{let O=z(y);if(!O)throw Error("useFlow must be used within a GetUserFeedbackProvider");let{client:G}=O,{flowId:R,prefetchOnMount:I=!1,hideCloseButton:A}=U.flow,v=F(()=>G.flow(R),[G,R]),E=U.mode==="container",_=q(null),N=M(v,(T)=>T,()=>D),b=K((T)=>!E?v.open({hideCloseButton:A,metadata:T?.metadata}):v.open({container:_.current??null,hideCloseButton:A,metadata:T?.metadata}),[v,A,E]),B=K(()=>v.close(),[v]),W=K((T)=>T?b():B(),[B,b]),X=K((T)=>{if(!E)return;_.current=T,v.setContainer(T)},[v,E]),Y=P(N),Z=E&&(N.isOpen||N.isLoading&&Y);return L(()=>{if(!I)return;v.prefetch()},[v,I]),{isOpen:N.isOpen,isLoading:N.isLoading,width:N.width,height:N.height,shouldRenderContainer:Z,setOpen:W,open:b,close:B,prerender:()=>v.prerender({hideCloseButton:A}),containerRef:X}};function m(U){let G=(U.container??"default")==="custom"?"container":"default",R=h({flow:{flowId:U.flowId,prefetchOnMount:U.prefetchOnMount,hideCloseButton:U.hideCloseButton},mode:G});if(G==="container")return{container:"custom",isOpen:R.isOpen,isLoading:R.isLoading,width:R.width,height:R.height,shouldRenderContainer:R.shouldRenderContainer,open:R.open,setOpen:R.setOpen,prerender:R.prerender,containerRef:R.containerRef};return{container:"default",isOpen:R.isOpen,isLoading:R.isLoading,width:R.width,height:R.height,open:R.open,close:R.close,prerender:R.prerender}}function d(){let U=z(y);if(!U)throw Error("useDefaultFlowContainer must be used within a GetUserFeedbackProvider");let{client:O,defaultFlowContainerState:G,registerDefaultFlowContainerConsumer:R,setDefaultFlowContainer:I}=U;L(()=>R(),[R]);let A=K((_)=>{I(_)},[I]),v=K(()=>O.close(),[O]),E=K((_)=>{if(_)return Promise.resolve();return v()},[v]);return{isOpen:G.isOpen,isLoading:G.isLoading,shouldRenderContainer:G.shouldRenderContainer,width:G.width,height:G.height,setOpen:E,containerRef:A}}function x(){let U=z(y);if(!U)throw Error("useFeedback must be used within a GetUserFeedbackProvider");let[O,G]=H(0),[R,I]=H(0),A=K(async(E)=>{G((_)=>_+1);try{return await U.client.feedback.fetch(E)}finally{G((_)=>_-1)}},[U.client]),v=K(async(E)=>{I((_)=>_+1);try{return await U.client.feedback.submit(E)}finally{I((_)=>_-1)}},[U.client]);return{fetch:A,isFetching:O>0,isSubmitting:R>0,submit:v}}function f(){let U=z(y);if(!U)throw Error("useGetUserFeedback must be used within a GetUserFeedbackProvider");return U.client}var Q="0.6.10".trim(),S=Q.length>0?Q:"0.0.0-local";export{f as useGetUserFeedback,m as useFlow,x as useFeedback,d as useDefaultFlowContainer,S as REACT_SDK_VERSION,w as GetUserFeedbackProvider};
|
|
3
3
|
|
|
4
|
-
//# debugId=
|
|
4
|
+
//# debugId=0480976B43E9968064756E2164756E21
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"\"use client\";\n\nimport {\n\ttype Client,\n\ttype ClientOptions,\n\tcreateClient,\n\ttype FeedbackInput,\n\ttype FetchFeedbackInput,\n\ttype FetchFeedbackResult,\n\ttype FlowState,\n\ttype OpenFlowOptions as SdkOpenFlowOptions,\n\ttype SubmitFeedbackResult,\n} from \"@getuserfeedback/sdk\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\n\n/** Return value for useFeedback(). */\nexport interface UseFeedbackReturn {\n\t/** Fetch feedback matching each selector for the JWT-authenticated user. */\n\tfetch: (input: FetchFeedbackInput) => Promise<FetchFeedbackResult>;\n\t/** True while one or more fetches from this hook are pending. */\n\tisFetching: boolean;\n\t/** True while one or more submissions from this hook are pending. */\n\tisSubmitting: boolean;\n\t/** Submit feedback about the current app or a specific target. */\n\tsubmit: (input: FeedbackInput) => Promise<SubmitFeedbackResult>;\n}\n\n/**\n * Options for useFlow().\n */\nexport interface UseFlowOptions {\n\t/** The flow/survey ID to control. */\n\tflowId: string;\n\t/** Prefetch the flow resources on mount so it opens faster. Default `false`. */\n\tprefetchOnMount?: boolean;\n\t/**\n\t * Where the flow mounts: `\"default\"` (floating container) or `\"custom\"` (your container via containerRef).\n\t * Default `\"default\"`.\n\t */\n\tcontainer?: \"default\" | \"custom\";\n\t/** When true, the view does not show a close button. */\n\thideCloseButton?: boolean;\n}\n\n/**\n * Per-open options for useFlow(). These apply to the specific response created\n * by that open action and do not affect prerendering.\n */\nexport interface UseFlowOpenOptions {\n\t/** Optional response metadata to preserve with the eventual submission. */\n\tmetadata?: SdkOpenFlowOptions[\"metadata\"];\n}\n\n/**\n * Return value for useFlow() when container is `\"default\"`: state and callbacks for the floating flow.\n */\nexport interface UseFlowReturn {\n\treadonly container: \"default\";\n\t/** True when the flow is visible. */\n\tisOpen: boolean;\n\t/** True after open is requested while the flow is not visible yet. */\n\tisLoading: boolean;\n\t/** Open the flow. */\n\topen: (options?: UseFlowOpenOptions) => Promise<void>;\n\t/** Close the flow. */\n\tclose: () => Promise<void>;\n\t/** Prerendering warms up the flow for instant display later. */\n\tprerender: () => Promise<void>;\n\t/** Flow width in pixels (when known). */\n\twidth?: number;\n\t/** Flow height in pixels (when known). */\n\theight?: number;\n}\n\n/**\n * Return value for useFlow() when container is `\"custom\"`: state and callbacks for host-managed container flows.\n */\nexport interface UseFlowContainerReturn {\n\treadonly container: \"custom\";\n\t/** True when the flow is visible. */\n\tisOpen: boolean;\n\t/** True after open is requested while the flow is not visible yet. */\n\tisLoading: boolean;\n\t/** True when your host UI should render the custom container element. */\n\tshouldRenderContainer: boolean;\n\t/** Open the flow in the custom container. */\n\topen: (options?: UseFlowOpenOptions) => Promise<void>;\n\t/** Open the flow when `true`, close when `false`. */\n\tsetOpen: (next: boolean) => Promise<void>;\n\t/** Prerendering warms up the flow for instant display later. */\n\tprerender: () => Promise<void>;\n\t/** Ref callback: attach this to the container element where the flow should render. */\n\tcontainerRef: (element: HTMLDivElement | null) => void;\n\t/** Flow width in pixels (when known). */\n\twidth?: number;\n\t/** Flow height in pixels (when known). */\n\theight?: number;\n}\n\n/**\n * Return value for useDefaultFlowContainer(): container ref and aggregate flow state\n * for all flows in the same provider subtree (including targeting-engine opens).\n */\nexport interface UseDefaultFlowContainerReturn {\n\t/** True when any flow is visible. */\n\tisOpen: boolean;\n\t/** True after any flow open is requested while the flow is not visible yet. */\n\tisLoading: boolean;\n\t/** True when your host UI should render the default flow container. */\n\tshouldRenderContainer: boolean;\n\t/** Active flow width in pixels (when known). */\n\twidth?: number;\n\t/** Active flow height in pixels (when known). */\n\theight?: number;\n\t/** Close currently displayed flows when `false`; opening is flow-driven. */\n\tsetOpen: (next: boolean) => Promise<void>;\n\t/** Ref callback for the default flow container element. */\n\tcontainerRef: (element: HTMLDivElement | null) => void;\n}\n\ntype InstanceFlowRenderState = {\n\tisOpen: boolean;\n\tisLoading: boolean;\n\twidth?: number;\n\theight?: number;\n};\n\ntype DefaultFlowContainerState = {\n\tisOpen: boolean;\n\tisLoading: boolean;\n\tshouldRenderContainer: boolean;\n\twidth?: number;\n\theight?: number;\n};\n\nconst DEFAULT_FLOW_CONTAINER_STATE: DefaultFlowContainerState = {\n\tisOpen: false,\n\tisLoading: false,\n\tshouldRenderContainer: false,\n\twidth: undefined,\n\theight: undefined,\n};\n\nconst toInstanceFlowRenderState = (\n\tstate: FlowState,\n): InstanceFlowRenderState => ({\n\tisOpen: state.isOpen,\n\tisLoading: state.isLoading,\n\twidth: state.width,\n\theight: state.height,\n});\n\nconst isSameFlowState = (\n\ta: Pick<FlowState, \"isOpen\" | \"isLoading\" | \"width\" | \"height\">,\n\tb: Pick<FlowState, \"isOpen\" | \"isLoading\" | \"width\" | \"height\">,\n): boolean =>\n\ta.isOpen === b.isOpen &&\n\ta.isLoading === b.isLoading &&\n\ta.width === b.width &&\n\ta.height === b.height;\n\ntype FlowStateStore = {\n\tgetFlowState: () => FlowState;\n\tsubscribeFlowState: (\n\t\tlistener: (state: FlowState) => void,\n\t\toptions?: { emitInitial?: boolean },\n\t) => () => void;\n};\n\nconst FALLBACK_FLOW_STATE: FlowState = {\n\tisOpen: false,\n\tisLoading: false,\n\twidth: undefined,\n\theight: undefined,\n};\n\nconst hasPreparedFlowState = (state: FlowState): boolean =>\n\ttypeof state.width === \"number\" && typeof state.height === \"number\";\n\nfunction useStableFlowStateSnapshot(store: FlowStateStore): () => FlowState {\n\tconst snapshotRef = useRef<FlowState>(store.getFlowState());\n\n\treturn useCallback((): FlowState => {\n\t\tconst next = store.getFlowState();\n\t\tconst previous = snapshotRef.current;\n\t\tif (isSameFlowState(previous, next)) {\n\t\t\treturn previous;\n\t\t}\n\t\tsnapshotRef.current = next;\n\t\treturn next;\n\t}, [store]);\n}\n\nfunction useFlowStateStoreValue<TState>(\n\tstore: FlowStateStore,\n\tproject: (state: FlowState) => TState,\n\tgetServerSnapshot: () => TState,\n): TState {\n\tconst getFlowStateSnapshot = useStableFlowStateSnapshot(store);\n\tconst lastSourceSnapshotRef = useRef<FlowState | null>(null);\n\tconst lastProjectedSnapshotRef = useRef<TState | null>(null);\n\tconst getProjectedSnapshot = useCallback((): TState => {\n\t\tconst sourceSnapshot = getFlowStateSnapshot();\n\t\tif (lastSourceSnapshotRef.current === sourceSnapshot) {\n\t\t\tconst cachedProjectedSnapshot = lastProjectedSnapshotRef.current;\n\t\t\tif (cachedProjectedSnapshot !== null) {\n\t\t\t\treturn cachedProjectedSnapshot;\n\t\t\t}\n\t\t}\n\t\tconst projectedSnapshot = project(sourceSnapshot);\n\t\tlastSourceSnapshotRef.current = sourceSnapshot;\n\t\tlastProjectedSnapshotRef.current = projectedSnapshot;\n\t\treturn projectedSnapshot;\n\t}, [getFlowStateSnapshot, project]);\n\n\treturn useSyncExternalStore<TState>(\n\t\t(onStoreChange) =>\n\t\t\tstore.subscribeFlowState(\n\t\t\t\t() => {\n\t\t\t\t\tonStoreChange();\n\t\t\t\t},\n\t\t\t\t{ emitInitial: false },\n\t\t\t),\n\t\tgetProjectedSnapshot,\n\t\tgetServerSnapshot,\n\t);\n}\n\ntype GetUserFeedbackContextValue = {\n\tclient: Client;\n\tapiKey: string;\n\tsetDefaultFlowContainer: (element: HTMLElement | null) => void;\n\tregisterDefaultFlowContainerConsumer: () => () => void;\n\tdefaultFlowContainerState: DefaultFlowContainerState;\n};\n\nconst GetUserFeedbackContext =\n\tcreateContext<GetUserFeedbackContextValue | null>(null);\n\nexport interface GetUserFeedbackProviderProps {\n\t/** Application subtree that can use getuserfeedback hooks. */\n\tchildren: ReactNode;\n\t/** Core SDK client options used to create/reuse the underlying client. */\n\tclientOptions: ClientOptions;\n}\n\n/**\n * Provider for getuserfeedback React hooks.\n * Wrap your app (or a subtree) so `useFeedback`, `useFlow`,\n * `useDefaultFlowContainer`, and `useGetUserFeedback` can access a client\n * created from `clientOptions`.\n *\n * If your app uses `useDefaultFlowContainer()`, all flows in this subtree\n * (including targeting-engine opens) target that container.\n * If the container ref is not mounted yet, loader keeps flows parked until it is.\n *\n * @example Basic usage\n * ```tsx\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <Routes />\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n *\n * @example Display *any* flow in your own dialog (a shadcn/ui dialog in this example):\n * ```tsx\n * function DefaultFlowContainer() {\n * const { containerRef, shouldRenderContainer, width, height, setOpen } = useDefaultFlowContainer();\n *\n * return (\n * <Dialog open={shouldRenderContainer} onOpenChange={setOpen}>\n * <DialogContent ref={containerRef} style={{ width, height }} />\n * </Dialog>\n * );\n * }\n *\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <AppRoutes />\n * <DefaultFlowContainer /> // Flows will be triggered by the targeting engine and displayed in your own dialog\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n */\nexport function GetUserFeedbackProvider({\n\tchildren,\n\tclientOptions,\n}: GetUserFeedbackProviderProps): ReactElement {\n\tconst client = createClient(clientOptions);\n\tconst defaultFlowContainerRef = useRef<HTMLElement | null>(null);\n\tconst defaultFlowContainerConsumerCountRef = useRef(0);\n\tconst instanceFlowState = useFlowStateStoreValue<InstanceFlowRenderState>(\n\t\tclient,\n\t\ttoInstanceFlowRenderState,\n\t\t() => DEFAULT_FLOW_CONTAINER_STATE,\n\t);\n\n\tconst setDefaultFlowContainer = useCallback(\n\t\t(element: HTMLElement | null) => {\n\t\t\tdefaultFlowContainerRef.current = element;\n\t\t\tif (defaultFlowContainerConsumerCountRef.current === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclient.setDefaultContainerPolicy({\n\t\t\t\tkind: \"hostContainer\",\n\t\t\t\thost: element,\n\t\t\t\tsharing: \"perFlowRun\",\n\t\t\t});\n\t\t},\n\t\t[client],\n\t);\n\n\tconst registerDefaultFlowContainerConsumer = useCallback(() => {\n\t\tdefaultFlowContainerConsumerCountRef.current += 1;\n\t\tif (defaultFlowContainerConsumerCountRef.current === 1) {\n\t\t\tclient.setDefaultContainerPolicy({\n\t\t\t\tkind: \"hostContainer\",\n\t\t\t\thost: defaultFlowContainerRef.current,\n\t\t\t\tsharing: \"perFlowRun\",\n\t\t\t});\n\t\t}\n\t\treturn () => {\n\t\t\tdefaultFlowContainerConsumerCountRef.current = Math.max(\n\t\t\t\t0,\n\t\t\t\tdefaultFlowContainerConsumerCountRef.current - 1,\n\t\t\t);\n\t\t\tif (defaultFlowContainerConsumerCountRef.current === 0) {\n\t\t\t\tclient.setDefaultContainerPolicy({ kind: \"floating\" });\n\t\t\t}\n\t\t};\n\t}, [client]);\n\n\tconst defaultFlowContainerState = useMemo<DefaultFlowContainerState>(() => {\n\t\tif (!instanceFlowState.isOpen && !instanceFlowState.isLoading) {\n\t\t\treturn DEFAULT_FLOW_CONTAINER_STATE;\n\t\t}\n\t\treturn {\n\t\t\tisOpen: instanceFlowState.isOpen,\n\t\t\tisLoading: instanceFlowState.isLoading,\n\t\t\tshouldRenderContainer: true,\n\t\t\twidth: instanceFlowState.width,\n\t\t\theight: instanceFlowState.height,\n\t\t};\n\t}, [instanceFlowState]);\n\n\tconst contextValue = useMemo(\n\t\t() => ({\n\t\t\tclient,\n\t\t\tapiKey: clientOptions.apiKey,\n\t\t\tsetDefaultFlowContainer,\n\t\t\tregisterDefaultFlowContainerConsumer,\n\t\t\tdefaultFlowContainerState,\n\t\t}),\n\t\t[\n\t\t\tclient,\n\t\t\tclientOptions.apiKey,\n\t\t\tdefaultFlowContainerState,\n\t\t\tregisterDefaultFlowContainerConsumer,\n\t\t\tsetDefaultFlowContainer,\n\t\t],\n\t);\n\n\treturn (\n\t\t<GetUserFeedbackContext.Provider value={contextValue}>\n\t\t\t{children}\n\t\t</GetUserFeedbackContext.Provider>\n\t);\n}\n\ntype UseFlowControllerOptions = {\n\tflow: UseFlowOptions;\n\tmode: \"default\" | \"container\";\n};\n\ntype UseFlowControllerReturn = {\n\tisOpen: boolean;\n\tisLoading: boolean;\n\twidth?: number;\n\theight?: number;\n\tshouldRenderContainer: boolean;\n\tsetOpen: (next: boolean) => Promise<void>;\n\topen: (options?: UseFlowOpenOptions) => Promise<void>;\n\tclose: () => Promise<void>;\n\tprerender: () => Promise<void>;\n\tcontainerRef: (element: HTMLDivElement | null) => void;\n};\n\nconst useFlowController = (\n\toptions: UseFlowControllerOptions,\n): UseFlowControllerReturn => {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useFlow must be used within a GetUserFeedbackProvider\");\n\t}\n\tconst { client } = ctx;\n\tconst { flowId, prefetchOnMount = false, hideCloseButton } = options.flow;\n\tconst flowRun = useMemo(() => client.flow(flowId), [client, flowId]);\n\tconst requiresContainer = options.mode === \"container\";\n\tconst customContainerRef = useRef<HTMLDivElement | null>(null);\n\n\t/** Flow state from loader events; open/loading + dimensions. */\n\tconst flowState = useFlowStateStoreValue<FlowState>(\n\t\tflowRun,\n\t\t(state) => state,\n\t\t() => FALLBACK_FLOW_STATE,\n\t);\n\n\tconst open = useCallback(\n\t\t(openOptions?: UseFlowOpenOptions): Promise<void> =>\n\t\t\t!requiresContainer\n\t\t\t\t? flowRun.open({\n\t\t\t\t\t\thideCloseButton,\n\t\t\t\t\t\tmetadata: openOptions?.metadata,\n\t\t\t\t\t})\n\t\t\t\t: flowRun.open({\n\t\t\t\t\t\tcontainer: customContainerRef.current ?? null,\n\t\t\t\t\t\thideCloseButton,\n\t\t\t\t\t\tmetadata: openOptions?.metadata,\n\t\t\t\t\t}),\n\t\t[flowRun, hideCloseButton, requiresContainer],\n\t);\n\n\tconst close = useCallback((): Promise<void> => flowRun.close(), [flowRun]);\n\n\tconst setOpen = useCallback(\n\t\t(next: boolean): Promise<void> => (next ? open() : close()),\n\t\t[close, open],\n\t);\n\n\tconst containerRef = useCallback(\n\t\t(element: HTMLDivElement | null): void => {\n\t\t\tif (!requiresContainer) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcustomContainerRef.current = element;\n\t\t\tflowRun.setContainer(element);\n\t\t},\n\t\t[flowRun, requiresContainer],\n\t);\n\n\tconst hasPreparedCustomView = hasPreparedFlowState(flowState);\n\tconst shouldRenderContainer =\n\t\trequiresContainer &&\n\t\t(flowState.isOpen || (flowState.isLoading && hasPreparedCustomView));\n\n\tuseEffect(() => {\n\t\tif (!prefetchOnMount) {\n\t\t\treturn;\n\t\t}\n\t\tflowRun.prefetch();\n\t}, [flowRun, prefetchOnMount]);\n\n\treturn {\n\t\tisOpen: flowState.isOpen,\n\t\tisLoading: flowState.isLoading,\n\t\twidth: flowState.width,\n\t\theight: flowState.height,\n\t\tshouldRenderContainer,\n\t\tsetOpen,\n\t\topen,\n\t\tclose,\n\t\tprerender: () => flowRun.prerender({ hideCloseButton }),\n\t\tcontainerRef,\n\t};\n};\n\n/**\n * @name useFlow\n * @description Open a Widget survey in the default floating container or your own container.\n * @example Default (floating) container\n * ```tsx\n * const { open, isLoading } = useFlow({ flowId: \"sur_123\" });\n * await open({\n * metadata: {\n * tags: {\n * journey_stage: \"onboarding\",\n * },\n * },\n * });\n * ```\n * @example Custom container\n * ```tsx\n * const { containerRef, shouldRenderContainer, open } = useFlow({ flowId: \"sur_123\", container: \"custom\" });\n * ```\n */\nexport function useFlow(\n\toptions: UseFlowOptions & { container?: \"default\" },\n): UseFlowReturn;\nexport function useFlow(\n\toptions: UseFlowOptions & { container: \"custom\" },\n): UseFlowContainerReturn;\nexport function useFlow(\n\toptions: UseFlowOptions,\n): UseFlowReturn | UseFlowContainerReturn {\n\tconst container = options.container ?? \"default\";\n\tconst mode = container === \"custom\" ? \"container\" : \"default\";\n\tconst controller = useFlowController({\n\t\tflow: {\n\t\t\tflowId: options.flowId,\n\t\t\tprefetchOnMount: options.prefetchOnMount,\n\t\t\thideCloseButton: options.hideCloseButton,\n\t\t},\n\t\tmode,\n\t});\n\n\tif (mode === \"container\") {\n\t\treturn {\n\t\t\tcontainer: \"custom\",\n\t\t\tisOpen: controller.isOpen,\n\t\t\tisLoading: controller.isLoading,\n\t\t\twidth: controller.width,\n\t\t\theight: controller.height,\n\t\t\tshouldRenderContainer: controller.shouldRenderContainer,\n\t\t\topen: controller.open,\n\t\t\tsetOpen: controller.setOpen,\n\t\t\tprerender: controller.prerender,\n\t\t\tcontainerRef: controller.containerRef,\n\t\t};\n\t}\n\n\treturn {\n\t\tcontainer: \"default\",\n\t\tisOpen: controller.isOpen,\n\t\tisLoading: controller.isLoading,\n\t\twidth: controller.width,\n\t\theight: controller.height,\n\t\topen: controller.open,\n\t\tclose: controller.close,\n\t\tprerender: controller.prerender,\n\t};\n}\n\n/**\n * @name useDefaultFlowContainer\n * @description Register and control a default flow container for all flows in this provider subtree.\n * @example Display any flow in your own dialog\n * ```tsx\n * function DefaultFlowContainer() {\n * const { containerRef, shouldRenderContainer, width, height, setOpen } = useDefaultFlowContainer();\n * return (\n * <Dialog open={shouldRenderContainer} onOpenChange={setOpen}>\n * <DialogContent ref={containerRef} style={{ width, height }} />\n * </Dialog>\n * );\n * }\n *\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <AppRoutes />\n * <DefaultFlowContainer />\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n */\nexport function useDefaultFlowContainer(): UseDefaultFlowContainerReturn {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\n\t\t\t\"useDefaultFlowContainer must be used within a GetUserFeedbackProvider\",\n\t\t);\n\t}\n\n\tconst {\n\t\tclient,\n\t\tdefaultFlowContainerState,\n\t\tregisterDefaultFlowContainerConsumer,\n\t\tsetDefaultFlowContainer,\n\t} = ctx;\n\n\tuseEffect(\n\t\t() => registerDefaultFlowContainerConsumer(),\n\t\t[registerDefaultFlowContainerConsumer],\n\t);\n\n\tconst containerRef = useCallback(\n\t\t(element: HTMLDivElement | null): void => {\n\t\t\tsetDefaultFlowContainer(element);\n\t\t},\n\t\t[setDefaultFlowContainer],\n\t);\n\n\tconst close = useCallback((): Promise<void> => client.close(), [client]);\n\n\tconst setOpen = useCallback(\n\t\t(next: boolean): Promise<void> => {\n\t\t\tif (next) {\n\t\t\t\treturn Promise.resolve();\n\t\t\t}\n\t\t\treturn close();\n\t\t},\n\t\t[close],\n\t);\n\n\treturn {\n\t\tisOpen: defaultFlowContainerState.isOpen,\n\t\tisLoading: defaultFlowContainerState.isLoading,\n\t\tshouldRenderContainer: defaultFlowContainerState.shouldRenderContainer,\n\t\twidth: defaultFlowContainerState.width,\n\t\theight: defaultFlowContainerState.height,\n\t\tsetOpen,\n\t\tcontainerRef,\n\t};\n}\n\n/**\n * Submit feedback or explicitly fetch the JWT-authenticated user's feedback.\n *\n * Neither operation runs automatically. Each promise returns the corresponding\n * SDK result and rejects when the operation fails.\n */\nexport function useFeedback(): UseFeedbackReturn {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\n\t\t\t\"useFeedback must be used within a GetUserFeedbackProvider\",\n\t\t);\n\t}\n\n\tconst [pendingFetchCount, setPendingFetchCount] = useState(0);\n\tconst [pendingSubmissionCount, setPendingSubmissionCount] = useState(0);\n\tconst fetch = useCallback(\n\t\tasync (input: FetchFeedbackInput): Promise<FetchFeedbackResult> => {\n\t\t\tsetPendingFetchCount((count) => count + 1);\n\t\t\ttry {\n\t\t\t\treturn await ctx.client.feedback.fetch(input);\n\t\t\t} finally {\n\t\t\t\tsetPendingFetchCount((count) => count - 1);\n\t\t\t}\n\t\t},\n\t\t[ctx.client],\n\t);\n\tconst submit = useCallback(\n\t\tasync (input: FeedbackInput): Promise<SubmitFeedbackResult> => {\n\t\t\tsetPendingSubmissionCount((count) => count + 1);\n\t\t\ttry {\n\t\t\t\treturn await ctx.client.feedback.submit(input);\n\t\t\t} finally {\n\t\t\t\tsetPendingSubmissionCount((count) => count - 1);\n\t\t\t}\n\t\t},\n\t\t[ctx.client],\n\t);\n\n\treturn {\n\t\tfetch,\n\t\tisFetching: pendingFetchCount > 0,\n\t\tisSubmitting: pendingSubmissionCount > 0,\n\t\tsubmit,\n\t};\n}\n\n/**\n * Hook for accessing the getuserfeedback client instance.\n *\n * @see https://getuserfeedback.com/docs/reference/react-sdk-reference\n * @see https://getuserfeedback.com/docs/reference/events\n *\n * @example Basic usage\n * ```tsx\n * const client = useGetUserFeedback();\n *\n * // on login\n * client.identify('user-123', { email: 'user@example.com' });\n *\n * // track a product event\n * client.track(\"Checkout Started\", { plan: \"pro\" });\n *\n * // on logout\n * client.reset();\n * ```\n *\n * @example Override color scheme\n * ```tsx\n * const client = useGetUserFeedback();\n * await client.configure({ colorScheme: \"dark\" });\n * ```\n */\nexport function useGetUserFeedback(): Client {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\n\t\t\t\"useGetUserFeedback must be used within a GetUserFeedbackProvider\",\n\t\t);\n\t}\n\treturn ctx.client;\n}\n",
|
|
6
6
|
"declare const __GX_REACT_SDK_VERSION__: string | undefined;\n\nconst reactSdkVersion =\n\ttypeof __GX_REACT_SDK_VERSION__ === \"string\"\n\t\t? __GX_REACT_SDK_VERSION__.trim()\n\t\t: \"\";\n\n// Build scripts inject __GX_REACT_SDK_VERSION__ for published artifacts.\n// Source-linked workspace usage may not inject defines, so keep a safe fallback.\nexport const REACT_SDK_VERSION =\n\treactSdkVersion.length > 0 ? reactSdkVersion : \"0.0.0-local\";\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": "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
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "0480976B43E9968064756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getuserfeedback/react",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.10",
|
|
4
4
|
"description": "getuserfeedback React SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"getuserfeedback",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"lint": "biome check ."
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@getuserfeedback/sdk": "^0.9.
|
|
44
|
+
"@getuserfeedback/sdk": "^0.9.8"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"react": ">=18.0.0"
|