@dismissible/react-client 0.3.2 → 1.0.0-canary.4.a9fb4a5

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 CHANGED
@@ -1,24 +1,37 @@
1
- # @dismissible/react-client
1
+ <p align="center">
2
+ <a href="https://dismissible.io" target="_blank"><img src="https://raw.githubusercontent.com/DismissibleIo/dismissible-api/main/docs/images/dismissible_logo.png" width="240" alt="Dismissible" /></a>
3
+ </p>
4
+
5
+ <p align="center">Never Show The Same Thing Twice!</p>
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/@dismissible/react-client" target="_blank"><img src="https://img.shields.io/npm/v/@dismissible/react-client.svg" alt="NPM Version" /></a>
8
+ <a href="https://github.com/dismissibleio/dismissible-react-client/blob/main/LICENSE" target="_blank"><img src="https://img.shields.io/npm/l/@dismissible/react-client.svg" alt="Package License" /></a>
9
+ <a href="https://www.npmjs.com/package/@dismissible/react-client" target="_blank"><img src="https://img.shields.io/npm/dm/@dismissible/react-client.svg" alt="NPM Downloads" /></a>
10
+ <a href="https://github.com/dismissibleio/dismissible-react-client" target="_blank"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/dismissibleio/dismissible-react-client/publish.yml" /></a>
11
+ <a href="https://paypal.me/joshstuartx" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" /></a>
12
+ </p>
13
+
14
+ Dismissible manages the state of your UI elements across sessions, so your users see what matters, once! No more onboarding messages reappearing on every tab, no more notifications haunting users across devices. Dismissible syncs dismissal state everywhere, so every message is intentional, never repetitive.
2
15
 
3
- A React component library for creating dismissible UI elements with persistent state management.
16
+ # @dismissible/react-client
4
17
 
5
- Use this in combination with [dismissible.io](https://dismissible.io). Get your free account now!
18
+ This is the React component library for creating dismissible UI elements with persistent state management.
6
19
 
7
- 🌐 **[dismissible.io](https://dismissible.io)** | 💰 **[View Pricing](https://dismissible.io/pricing)** | 📖 **[Documentation](https://docs.dismissible.io)**
20
+ This component is used with the [Dismissible API Server](https://github.com/DismissibleIo/dismissible-api), which you can self-host with Docker or integrate into your NestJS application.
8
21
 
9
- [![npm version](https://badge.fury.io/js/@dismissible%2Freact-client.svg)](https://badge.fury.io/js/@dismissible%2Freact-client)
10
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
22
+ **[dismissible.io](https://dismissible.io)** | **[Documentation](https://dismissible.io/docs)** | **[API Server](https://github.com/DismissibleIo/dismissible-api)**
11
23
 
12
24
  ## Features
13
25
 
14
- - 🎯 **Easy to use** - Simple component API for dismissible content
15
- - 💾 **Persistent state** - Dismissal state is saved and restored across sessions
16
- - 🔐 **JWT Authentication** - Built-in support for JWT-based user authentication (Enterprise only)
17
- - 🎨 **Customizable** - Custom loading, error, and dismiss button components
18
- - **Accessible** - Built with accessibility best practices
19
- - 🪝 **Hook-based** - Includes `useDismissibleItem` hook for custom implementations
20
- - 📦 **Lightweight** - Minimal bundle size with tree-shaking support
21
- - 🔧 **TypeScript** - Full TypeScript support with complete type definitions
26
+ - **Easy to use** - Simple component API for dismissible content
27
+ - **Persistent state** - Dismissal state is saved and restored across sessions when using the [Dismissible API Server](https://github.com/DismissibleIo/dismissible-api)
28
+ - **Restore support** - Restore previously dismissed items programmatically
29
+ - **JWT Authentication** - Built-in support for secure JWT-based authentication
30
+ - **Customizable** - Custom loading, error, and dismiss button components
31
+ - **Accessible** - Built with accessibility best practices
32
+ - **Hook-based** - Includes `useDismissibleItem` hook for custom implementations
33
+ - **Lightweight** - Minimal bundle size with tree-shaking support
34
+ - **TypeScript** - Full TypeScript support with complete type definitions
22
35
 
23
36
  ## Installation
24
37
 
@@ -36,13 +49,61 @@ npm install react react-dom
36
49
 
37
50
  ## Quick Start
38
51
 
52
+ ### 1. Set up the Dismissible API Server
53
+
54
+ First, you need a [Dismissible API Server](https://github.com/DismissibleIo/dismissible-api). The easiest way is with Docker:
55
+
56
+ ```yaml
57
+ # docker-compose.yml
58
+ version: '3.8'
59
+ services:
60
+ api:
61
+ image: dismissibleio/dismissible-api:latest
62
+ ports:
63
+ - '3001:3001'
64
+ environment:
65
+ DISMISSIBLE_PORT: 3001
66
+ ```
67
+
68
+ ```bash
69
+ docker-compose up -d
70
+ ```
71
+
72
+ OR
73
+
74
+ ```bash
75
+ docker run -p 3001:3001 -e DISMISSIBLE_PORT=3001 dismissibleio/dismissible-api:latest
76
+ ```
77
+
78
+ See the [API Server documentation](https://github.com/DismissibleIo/dismissible-api) for more deployment options including NestJS integration, public Docker image and more.
79
+
80
+ ### 2. Configure the Provider
81
+
82
+ Wrap your app with `DismissibleProvider`. The `userId` prop is **required** to track all your dismissals per user:
83
+
39
84
  ```tsx
40
- import React from 'react';
41
- import { Dismissible } from '@dismissible/react-client';
85
+ import { DismissibleProvider } from '@dismissible/react-client';
42
86
 
43
87
  function App() {
88
+ const userId = getCurrentUserId();
89
+
44
90
  return (
45
- <Dismissible id="welcome-banner-123-413-31-1">
91
+ <DismissibleProvider userId={userId} baseUrl="http://localhost:3001">
92
+ <YourApp />
93
+ </DismissibleProvider>
94
+ );
95
+ }
96
+ ```
97
+
98
+ ### 3. Use Dismissible Components
99
+ Now wrap any component you want to be dismissible with the `<Dismissible>` component, and the `itemId`, along with the `userId`, will become the unique key that is tracked across sessions and devices.
100
+
101
+ ```tsx
102
+ import { Dismissible } from '@dismissible/react-client';
103
+
104
+ function WelcomeBanner() {
105
+ return (
106
+ <Dismissible itemId="welcome-banner">
46
107
  <div className="banner">
47
108
  <h2>Welcome to our app!</h2>
48
109
  <p>This banner can be dismissed and won't show again.</p>
@@ -56,16 +117,15 @@ function App() {
56
117
 
57
118
  ### `<DismissibleProvider>` Component
58
119
 
59
- Context provider for JWT authentication and configuration. Wrap your app or components that need JWT authentication.
60
-
61
- > **Note:** JWT authentication is only available for **Enterprise customers**. For standard usage, you can use the `<Dismissible>` component directly without a provider. [View pricing →](https://dismissible.io/pricing)
120
+ Context provider that configures authentication and API settings. **Required** - all `<Dismissible>` components must be wrapped in a provider.
62
121
 
63
122
  #### Props
64
123
 
65
124
  | Prop | Type | Required | Description |
66
125
  |------|------|----------|-------------|
67
- | `jwt` | `string \| (() => string) \| (() => Promise<string>)` | | JWT token for user-specific dismissals (**Enterprise only**) |
68
- | `baseUrl` | `string` | ❌ | Custom API base URL override |
126
+ | `userId` | `string` | | User ID for tracking dismissals per user |
127
+ | `jwt` | `string \| (() => string) \| (() => Promise<string>)` | ❌ | JWT token for secure authentication |
128
+ | `baseUrl` | `string` | ❌ | API base URL (defaults to your self-hosted server) |
69
129
  | `children` | `ReactNode` | ✅ | Components that will use the dismissible functionality |
70
130
 
71
131
  #### Example
@@ -73,37 +133,53 @@ Context provider for JWT authentication and configuration. Wrap your app or comp
73
133
  ```tsx
74
134
  import { DismissibleProvider } from '@dismissible/react-client';
75
135
 
76
- // With static JWT
136
+ // Basic setup with userId
77
137
  function App() {
78
138
  return (
79
- <DismissibleProvider jwt="eyJhbGciOiJIUzI1NiIs...">
139
+ <DismissibleProvider userId="user-123" baseUrl="http://localhost:3001">
80
140
  <YourApp />
81
141
  </DismissibleProvider>
82
142
  );
83
143
  }
84
144
 
85
- // With dynamic JWT function
86
- function AppWithDynamicAuth() {
145
+ // With static JWT
146
+ function AppWithJWT() {
87
147
  return (
88
- <DismissibleProvider jwt={() => getAccessToken()}>
148
+ <DismissibleProvider
149
+ userId="user-123"
150
+ jwt="eyJhbGciOiJIUzI1NiIs..."
151
+ baseUrl="https://api.yourapp.com"
152
+ >
89
153
  <YourApp />
90
154
  </DismissibleProvider>
91
155
  );
92
156
  }
93
157
 
94
- // With async JWT function
95
- function AppWithAsyncAuth() {
158
+ // With dynamic JWT function
159
+ function AppWithDynamicAuth() {
160
+ const { user, getAccessToken } = useAuth();
161
+
96
162
  return (
97
- <DismissibleProvider jwt={async () => await fetchAccessToken()}>
163
+ <DismissibleProvider
164
+ userId={user.id}
165
+ jwt={() => getAccessToken()}
166
+ baseUrl="https://api.yourapp.com"
167
+ >
98
168
  <YourApp />
99
169
  </DismissibleProvider>
100
170
  );
101
171
  }
102
172
 
103
- // Without JWT (anonymous/backwards compatible)
104
- function AppWithoutAuth() {
173
+ // With async JWT function
174
+ function AppWithAsyncAuth() {
175
+ const { user, refreshAndGetToken } = useAuth();
176
+
105
177
  return (
106
- <DismissibleProvider>
178
+ <DismissibleProvider
179
+ userId={user.id}
180
+ jwt={async () => await refreshAndGetToken()}
181
+ baseUrl="https://api.yourapp.com"
182
+ >
107
183
  <YourApp />
108
184
  </DismissibleProvider>
109
185
  );
@@ -114,25 +190,29 @@ function AppWithoutAuth() {
114
190
 
115
191
  The main component for creating dismissible content.
116
192
 
193
+ > **Note:** The `<Dismissible>` component renders `null` when an item is dismissed. For restore functionality, use the `useDismissibleItem` hook directly in custom implementations.
194
+
117
195
  #### Props
118
196
 
119
197
  | Prop | Type | Required | Description |
120
198
  |------|------|----------|-------------|
121
- | `id` | `string` | ✅ | Unique identifier for the dismissible item |
199
+ | `itemId` | `string` | ✅ | Unique identifier for the dismissible item |
122
200
  | `children` | `ReactNode` | ✅ | Content to render when not dismissed |
123
201
  | `onDismiss` | `() => void` | ❌ | Callback fired when item is dismissed |
124
- | `LoadingComponent` | `ComponentType<{id: string}>` | ❌ | Custom loading component |
125
- | `ErrorComponent` | `ComponentType<{id: string, error: Error}>` | ❌ | Custom error component |
126
- | `DismissButtonComponent` | `ComponentType<{id: string, onDismiss: () => Promise<void>, ariaLabel: string}>` | ❌ | Custom dismiss button |
202
+ | `LoadingComponent` | `ComponentType<{itemId: string}>` | ❌ | Custom loading component |
203
+ | `ErrorComponent` | `ComponentType<{itemId: string, error: Error}>` | ❌ | Custom error component |
204
+ | `DismissButtonComponent` | `ComponentType<{onDismiss: () => Promise<void>, ariaLabel: string}>` | ❌ | Custom dismiss button |
127
205
  | `ignoreErrors` | `boolean` | ❌ | Ignore errors and display component anyway (default: false) |
206
+ | `enableCache` | `boolean` | ❌ | Enable localStorage caching (default: true) |
207
+ | `cachePrefix` | `string` | ❌ | Cache key prefix (default: 'dismissible') |
208
+ | `cacheExpiration` | `number` | ❌ | Cache expiration time in milliseconds |
128
209
 
129
210
  #### Example
130
211
 
131
212
  ```tsx
132
213
  <Dismissible
133
- id="promo-banner"
214
+ itemId="promo-banner"
134
215
  onDismiss={() => console.log('Banner dismissed')}
135
- onRestore={() => console.log('Banner restored')}
136
216
  >
137
217
  <div className="promo">
138
218
  <h3>Special Offer!</h3>
@@ -149,28 +229,36 @@ For custom implementations and advanced use cases.
149
229
 
150
230
  | Parameter | Type | Required | Description |
151
231
  |-----------|------|----------|-------------|
152
- | `id` | `string` | ✅ | Unique identifier for the dismissible item |
232
+ | `itemId` | `string` | ✅ | Unique identifier for the dismissible item |
233
+ | `options` | `object` | ❌ | Configuration options |
234
+
235
+ #### Options
236
+
237
+ | Option | Type | Required | Description |
238
+ |--------|------|----------|-------------|
239
+ | `enableCache` | `boolean` | ❌ | Enable localStorage caching (default: true) |
240
+ | `cachePrefix` | `string` | ❌ | Cache key prefix (default: 'dismissible') |
241
+ | `cacheExpiration` | `number` | ❌ | Cache expiration time in milliseconds |
242
+ | `initialData` | `IDismissibleItem` | ❌ | Initial data for the dismissible item |
153
243
 
154
244
  #### Returns
155
245
 
156
246
  | Property | Type | Description |
157
247
  |----------|------|-------------|
158
- | `dismissedOn` | `string \| null` | ISO date string when item was dismissed, or null |
248
+ | `dismissedAt` | `string \| undefined` | ISO date string when item was dismissed, or undefined |
159
249
  | `dismiss` | `() => Promise<void>` | Function to dismiss the item |
250
+ | `restore` | `() => Promise<void>` | Function to restore a dismissed item |
160
251
  | `isLoading` | `boolean` | Loading state indicator |
161
- | `error` | `Error \| null` | Error state, if any |
252
+ | `error` | `Error \| undefined` | Error state, if any |
253
+ | `item` | `IDismissibleItem \| undefined` | The full dismissible item object |
162
254
 
163
255
  #### Example
164
256
 
165
257
  ```tsx
166
258
  import { useDismissibleItem } from '@dismissible/react-client';
167
259
 
168
- function CustomDismissible({ id, children }) {
169
- const { dismissedOn, dismiss, isLoading, error } = useDismissibleItem(id);
170
-
171
- if (dismissedOn) {
172
- return null; // Item is dismissed
173
- }
260
+ function CustomDismissible({ itemId, children }) {
261
+ const { dismissedAt, dismiss, restore, isLoading, error } = useDismissibleItem(itemId);
174
262
 
175
263
  if (isLoading) {
176
264
  return <div>Loading...</div>;
@@ -180,6 +268,15 @@ function CustomDismissible({ id, children }) {
180
268
  return <div>Error: {error.message}</div>;
181
269
  }
182
270
 
271
+ if (dismissedAt) {
272
+ return (
273
+ <div>
274
+ <p>This item was dismissed.</p>
275
+ <button onClick={restore}>Restore</button>
276
+ </div>
277
+ );
278
+ }
279
+
183
280
  return (
184
281
  <div>
185
282
  {children}
@@ -193,21 +290,16 @@ function CustomDismissible({ id, children }) {
193
290
 
194
291
  ## Usage Examples
195
292
 
196
- ### JWT Authentication Setup (Enterprise Only)
197
-
198
- > **Enterprise Feature:** JWT authentication allows user-specific dismissible state management. This feature requires an Enterprise subscription. [Learn more about Enterprise features →](https://dismissible.io/pricing)
199
-
200
- For enterprise accounts that require user-specific dismissible state, wrap your app with the `DismissibleProvider`:
293
+ ### Basic Dismissible Banner
201
294
 
202
295
  ```tsx
203
296
  import { DismissibleProvider, Dismissible } from '@dismissible/react-client';
204
297
 
205
298
  function App() {
206
- // Example with static JWT token
207
- const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
299
+ const userId = getCurrentUserId();
208
300
 
209
301
  return (
210
- <DismissibleProvider jwt={jwt}>
302
+ <DismissibleProvider userId={userId} baseUrl="http://localhost:3001">
211
303
  <Dashboard />
212
304
  </DismissibleProvider>
213
305
  );
@@ -215,61 +307,47 @@ function App() {
215
307
 
216
308
  function Dashboard() {
217
309
  return (
218
- <div>
219
- {/* These dismissible items will be user-specific */}
220
- <Dismissible id="user-welcome-banner">
221
- <div className="alert alert-info">
222
- <h4>Welcome back!</h4>
223
- <p>You have 3 new notifications.</p>
224
- </div>
225
- </Dismissible>
226
- </div>
310
+ <Dismissible itemId="welcome-banner">
311
+ <div className="alert alert-info">
312
+ <h4>Welcome!</h4>
313
+ <p>Thanks for joining our platform. Here are some quick tips to get started.</p>
314
+ </div>
315
+ </Dismissible>
227
316
  );
228
317
  }
229
318
  ```
230
319
 
231
- ### Dynamic JWT with Authentication Provider
320
+ ### JWT Authentication Setup
321
+
322
+ For secure environments, configure JWT authentication:
232
323
 
233
324
  ```tsx
234
- import { DismissibleProvider } from '@dismissible/react-client';
235
- import { useAuth } from './auth'; // Your auth context
325
+ import { DismissibleProvider, Dismissible } from '@dismissible/react-client';
236
326
 
237
- // Synchronous JWT function
238
327
  function App() {
239
- const { getAccessToken } = useAuth();
328
+ const { user, getAccessToken } = useAuth();
240
329
 
241
330
  return (
242
- <DismissibleProvider jwt={() => getAccessToken()}>
243
- <YourApp />
244
- </DismissibleProvider>
245
- );
246
- }
247
-
248
- // Asynchronous JWT function
249
- function AppWithAsyncAuth() {
250
- const { refreshAndGetToken } = useAuth();
251
-
252
- return (
253
- <DismissibleProvider jwt={async () => await refreshAndGetToken()}>
254
- <YourApp />
331
+ <DismissibleProvider
332
+ userId={user.id}
333
+ jwt={() => getAccessToken()}
334
+ baseUrl="https://api.yourapp.com"
335
+ >
336
+ <Dashboard />
255
337
  </DismissibleProvider>
256
338
  );
257
339
  }
258
- ```
259
-
260
- ### Basic Dismissible Banner
261
-
262
- ```tsx
263
- import { Dismissible } from '@dismissible/react-client';
264
340
 
265
- function WelcomeBanner() {
341
+ function Dashboard() {
266
342
  return (
267
- <Dismissible id="welcome-banner-234-432-432-1">
268
- <div className="alert alert-info">
269
- <h4>Welcome!</h4>
270
- <p>Thanks for joining our platform. Here are some quick tips to get started.</p>
271
- </div>
272
- </Dismissible>
343
+ <div>
344
+ <Dismissible itemId="user-welcome-banner">
345
+ <div className="alert alert-info">
346
+ <h4>Welcome back!</h4>
347
+ <p>You have 3 new notifications.</p>
348
+ </div>
349
+ </Dismissible>
350
+ </div>
273
351
  );
274
352
  }
275
353
  ```
@@ -292,7 +370,7 @@ const CustomDismissButton = ({ onDismiss, ariaLabel }) => (
292
370
  function CustomBanner() {
293
371
  return (
294
372
  <Dismissible
295
- id="custom-banner"
373
+ itemId="custom-banner"
296
374
  DismissButtonComponent={CustomDismissButton}
297
375
  >
298
376
  <div className="banner">
@@ -308,7 +386,7 @@ function CustomBanner() {
308
386
  ```tsx
309
387
  import { Dismissible } from '@dismissible/react-client';
310
388
 
311
- const CustomLoader = ({ id }) => (
389
+ const CustomLoader = ({ itemId }) => (
312
390
  <div className="spinner">
313
391
  <div className="bounce1"></div>
314
392
  <div className="bounce2"></div>
@@ -329,7 +407,7 @@ const CustomError = ({ error }) => (
329
407
  function AdvancedBanner() {
330
408
  return (
331
409
  <Dismissible
332
- id="advanced-banner"
410
+ itemId="advanced-banner"
333
411
  LoadingComponent={CustomLoader}
334
412
  ErrorComponent={CustomError}
335
413
  >
@@ -349,21 +427,21 @@ import { Dismissible } from '@dismissible/react-client';
349
427
  function Dashboard() {
350
428
  return (
351
429
  <div>
352
- <Dismissible id="feature-announcement-234-432-432-1">
430
+ <Dismissible itemId="feature-announcement">
353
431
  <div className="alert alert-success">
354
- 🎉 New feature: Dark mode is now available!
432
+ New feature: Dark mode is now available!
355
433
  </div>
356
434
  </Dismissible>
357
435
 
358
- <Dismissible id="maintenance-notice-234-432-432-1">
436
+ <Dismissible itemId="maintenance-notice">
359
437
  <div className="alert alert-warning">
360
- ⚠️ Scheduled maintenance: Sunday 2AM-4AM EST
438
+ Scheduled maintenance: Sunday 2AM-4AM EST
361
439
  </div>
362
440
  </Dismissible>
363
441
 
364
- <Dismissible id="survey-request-234-432-432-1">
442
+ <Dismissible itemId="survey-request">
365
443
  <div className="alert alert-info">
366
- 📝 Help us improve! Take our 2-minute survey.
444
+ Help us improve! Take our 2-minute survey.
367
445
  </div>
368
446
  </Dismissible>
369
447
  </div>
@@ -371,35 +449,6 @@ function Dashboard() {
371
449
  }
372
450
  ```
373
451
 
374
- ### User-Specific vs Anonymous Dismissible Items
375
-
376
- The behavior changes based on whether JWT authentication is configured:
377
-
378
- > **Enterprise vs Standard:** JWT authentication for user-specific dismissals is an Enterprise feature. Standard accounts use anonymous (account-level) dismissals. [Compare plans →](https://dismissible.io/pricing)
379
-
380
- ```tsx
381
- import { DismissibleProvider, Dismissible } from '@dismissible/react-client';
382
-
383
- // With JWT - dismissible state is user-specific
384
- function AuthenticatedApp() {
385
- return (
386
- <DismissibleProvider jwt={() => getAccessToken()}>
387
- <div>
388
- {/* Each user will see this banner independently */}
389
- <Dismissible id="feature-announcement">
390
- <div>New feature available!</div>
391
- </Dismissible>
392
-
393
- {/* User A dismissing this won't affect User B */}
394
- <Dismissible id="survey-request">
395
- <div>Please take our survey!</div>
396
- </Dismissible>
397
- </div>
398
- </DismissibleProvider>
399
- );
400
- }
401
- ```
402
-
403
452
  ### Error Handling with ignoreErrors
404
453
 
405
454
  ```tsx
@@ -409,7 +458,7 @@ import { Dismissible } from '@dismissible/react-client';
409
458
  function RobustBanner() {
410
459
  return (
411
460
  <Dismissible
412
- id="important-announcement"
461
+ itemId="important-announcement"
413
462
  ignoreErrors={true}
414
463
  >
415
464
  <div className="important-banner">
@@ -421,76 +470,19 @@ function RobustBanner() {
421
470
  }
422
471
  ```
423
472
 
424
- ### Async JWT Authentication Examples
425
-
426
- ```tsx
427
- import { DismissibleProvider } from '@dismissible/react-client';
428
-
429
- // With token refresh logic
430
- function AppWithTokenRefresh() {
431
- return (
432
- <DismissibleProvider
433
- jwt={async () => {
434
- try {
435
- const token = await fetch('/api/auth/refresh', {
436
- method: 'POST',
437
- credentials: 'include'
438
- });
439
- const { accessToken } = await token.json();
440
- return accessToken;
441
- } catch (error) {
442
- console.error('Failed to refresh token:', error);
443
- throw error;
444
- }
445
- }}
446
- >
447
- <YourApp />
448
- </DismissibleProvider>
449
- );
450
- }
451
-
452
- // With Firebase Auth
453
- function AppWithFirebase() {
454
- return (
455
- <DismissibleProvider
456
- jwt={async () => {
457
- const user = firebase.auth().currentUser;
458
- if (user) {
459
- return await user.getIdToken();
460
- }
461
- throw new Error('User not authenticated');
462
- }}
463
- >
464
- <YourApp />
465
- </DismissibleProvider>
466
- );
467
- }
468
-
469
- // With Auth0
470
- function AppWithAuth0() {
471
- const { getAccessTokenSilently } = useAuth0();
472
-
473
- return (
474
- <DismissibleProvider jwt={async () => await getAccessTokenSilently()}>
475
- <YourApp />
476
- </DismissibleProvider>
477
- );
478
- }
479
- ```
480
-
481
473
  ### Using the Hook for Complex Logic
482
474
 
483
475
  ```tsx
484
- import { useDismissibleItem, DismissibleProvider } from '@dismissible/react-client';
476
+ import { useDismissibleItem } from '@dismissible/react-client';
485
477
  import { useState, useEffect } from 'react';
486
478
 
487
- function SmartNotification({ id, message, type = 'info' }) {
488
- const { dismissedOn, dismiss, isLoading } = useDismissibleItem(id);
479
+ function SmartNotification({ itemId, message, type = 'info' }) {
480
+ const { dismissedAt, dismiss, isLoading } = useDismissibleItem(itemId);
489
481
  const [autoHide, setAutoHide] = useState(false);
490
482
 
491
483
  // Auto-hide after 10 seconds for info messages
492
484
  useEffect(() => {
493
- if (type === 'info' && !dismissedOn) {
485
+ if (type === 'info' && !dismissedAt) {
494
486
  const timer = setTimeout(() => {
495
487
  setAutoHide(true);
496
488
  dismiss();
@@ -498,9 +490,9 @@ function SmartNotification({ id, message, type = 'info' }) {
498
490
 
499
491
  return () => clearTimeout(timer);
500
492
  }
501
- }, [type, dismissedOn, dismiss]);
493
+ }, [type, dismissedAt, dismiss]);
502
494
 
503
- if (dismissedOn || autoHide) {
495
+ if (dismissedAt || autoHide) {
504
496
  return null;
505
497
  }
506
498
 
@@ -517,17 +509,37 @@ function SmartNotification({ id, message, type = 'info' }) {
517
509
  </div>
518
510
  );
519
511
  }
512
+ ```
513
+
514
+ ### Restoring Dismissed Items
515
+
516
+ Use the `restore` function to bring back previously dismissed content:
517
+
518
+ ```tsx
519
+ import { useDismissibleItem } from '@dismissible/react-client';
520
+
521
+ function RestorableBanner({ itemId }) {
522
+ const { dismissedAt, dismiss, restore, isLoading } = useDismissibleItem(itemId);
523
+
524
+ if (dismissedAt) {
525
+ return (
526
+ <div className="dismissed-placeholder">
527
+ <p>Banner was dismissed on {new Date(dismissedAt).toLocaleDateString()}</p>
528
+ <button onClick={restore} disabled={isLoading}>
529
+ {isLoading ? 'Restoring...' : 'Show Banner Again'}
530
+ </button>
531
+ </div>
532
+ );
533
+ }
520
534
 
521
- // Usage with async authentication
522
- function App() {
523
535
  return (
524
- <DismissibleProvider jwt={async () => await getUserToken()}>
525
- <SmartNotification
526
- id="user-specific-notification"
527
- message="Welcome back!"
528
- type="info"
529
- />
530
- </DismissibleProvider>
536
+ <div className="banner">
537
+ <h3>Welcome!</h3>
538
+ <p>This is a restorable banner.</p>
539
+ <button onClick={dismiss} disabled={isLoading}>
540
+ Dismiss
541
+ </button>
542
+ </div>
531
543
  );
532
544
  }
533
545
  ```
@@ -555,37 +567,40 @@ The library includes minimal default styles. You can override them or provide yo
555
567
  }
556
568
  ```
557
569
 
558
- ## TypeScript Support
570
+ ## Self-Hosting
559
571
 
560
- The library is written in TypeScript and exports all type definitions:
572
+ Dismissible is designed to be self-hosted. You have full control over your data.
561
573
 
562
- ```tsx
563
- import type {
564
- DismissibleProps,
565
- DismissibleProviderProps,
566
- JwtToken,
567
- IDismissibleItem
568
- } from '@dismissible/react-client';
569
-
570
- // Dismissible component with custom props
571
- const MyComponent: React.FC<DismissibleProps> = (props) => {
572
- // Your component implementation
573
- };
574
-
575
- // Provider with JWT authentication
576
- const AuthProvider: React.FC<DismissibleProviderProps> = ({ children, jwt }) => {
577
- return (
578
- <DismissibleProvider jwt={jwt}>
579
- {children}
580
- </DismissibleProvider>
581
- );
582
- };
574
+ ### Option 1: Docker (Recommended)
575
+
576
+ The fastest way to get started:
577
+
578
+ ```bash
579
+ docker run -p 3001:3001 dismissibleio/dismissible-api:latest
583
580
  ```
584
581
 
582
+ See the [Docker documentation](https://dismissible.io/docs/docker) for production configuration.
583
+
584
+ ### Option 2: NestJS Module
585
+
586
+ Integrate directly into your existing NestJS application:
587
+
588
+ ```bash
589
+ npm install @dismissible/nestjs-api
590
+ ```
591
+
592
+ See the [NestJS documentation](https://dismissible.io/docs/nestjs) for setup instructions.
593
+
585
594
  ## Support
586
595
 
587
- - 📖 [Documentation](https://docs.dismissible.io)
596
+ - [Documentation](https://dismissible.io/docs)
597
+ - [GitHub - React Client](https://github.com/DismissibleIo/dismissible-react-client)
598
+ - [GitHub - API Server](https://github.com/DismissibleIo/dismissible-api)
599
+
600
+ ## License
601
+
602
+ MIT © [Dismissible](https://dismissible.io)
588
603
 
589
604
  ## Changelog
590
605
 
591
- See [CHANGELOG.md](./CHANGELOG.md) for a detailed list of changes.
606
+ See [CHANGELOG.md](./CHANGELOG.md) for a detailed list of changes.