@dismissible/react-client 0.3.2-canary.5.88e2777 → 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/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # 1.0.0 (2025-12-21)
2
+
3
+
4
+ ### Features
5
+
6
+ * **react-client:** dismissible React Client ([#1](https://github.com/DismissibleIo/dismissible-react-client/issues/1)) ([a4e6dc7](https://github.com/DismissibleIo/dismissible-react-client/commit/a4e6dc7786a4fd1abb81f4a796e22d37fdc12e0b))
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * **react-client:** This is the first public release which requires both the DismissibleProvider and
12
+ Dismissible components to be used to force a userId.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dismissible
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,26 +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>
2
13
 
3
- A React component library for creating dismissible UI elements with persistent state management.
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.
4
15
 
5
- **Free and open source** - use with the [Dismissible API Server](https://github.com/DismissibleIo/dismissible-api) that you can self-host with Docker or integrate into your NestJS application.
16
+ # @dismissible/react-client
6
17
 
7
- 🌐 **[dismissible.io](https://dismissible.io)** | 📖 **[Documentation](https://dismissible.io/docs)** | 🐙 **[API Server](https://github.com/DismissibleIo/dismissible-api)**
18
+ This is the React component library for creating dismissible UI elements with persistent state management.
8
19
 
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)
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.
21
+
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
- - 🔄 **Restore support** - Restore previously dismissed items programmatically
17
- - 🔐 **JWT Authentication** - Built-in support for secure JWT-based authentication
18
- - 🎨 **Customizable** - Custom loading, error, and dismiss button components
19
- - **Accessible** - Built with accessibility best practices
20
- - 🪝 **Hook-based** - Includes `useDismissibleItem` hook for custom implementations
21
- - 📦 **Lightweight** - Minimal bundle size with tree-shaking support
22
- - 🔧 **TypeScript** - Full TypeScript support with complete type definitions
23
- - 🐳 **Self-hosted** - Works with your own Dismissible API server
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
24
35
 
25
36
  ## Installation
26
37
 
@@ -40,7 +51,7 @@ npm install react react-dom
40
51
 
41
52
  ### 1. Set up the Dismissible API Server
42
53
 
43
- First, you need a Dismissible API server running. The easiest way is with Docker:
54
+ First, you need a [Dismissible API Server](https://github.com/DismissibleIo/dismissible-api). The easiest way is with Docker:
44
55
 
45
56
  ```yaml
46
57
  # docker-compose.yml
@@ -52,38 +63,29 @@ services:
52
63
  - '3001:3001'
53
64
  environment:
54
65
  DISMISSIBLE_PORT: 3001
55
- DISMISSIBLE_POSTGRES_STORAGE_CONNECTION_STRING: postgresql://postgres:postgres@db:5432/dismissible
56
- depends_on:
57
- - db
58
-
59
- db:
60
- image: postgres:15
61
- environment:
62
- POSTGRES_USER: postgres
63
- POSTGRES_PASSWORD: postgres
64
- POSTGRES_DB: dismissible
65
- volumes:
66
- - postgres_data:/var/lib/postgresql/data
67
-
68
- volumes:
69
- postgres_data:
70
66
  ```
71
67
 
72
68
  ```bash
73
69
  docker-compose up -d
74
70
  ```
75
71
 
76
- See the [API Server documentation](https://github.com/DismissibleIo/dismissible-api) for more deployment options including NestJS integration.
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.
77
79
 
78
80
  ### 2. Configure the Provider
79
81
 
80
- Wrap your app with `DismissibleProvider`. The `userId` prop is **required** to track dismissals per user:
82
+ Wrap your app with `DismissibleProvider`. The `userId` prop is **required** to track all your dismissals per user:
81
83
 
82
84
  ```tsx
83
85
  import { DismissibleProvider } from '@dismissible/react-client';
84
86
 
85
87
  function App() {
86
- const userId = getCurrentUserId(); // Get from your auth system
88
+ const userId = getCurrentUserId();
87
89
 
88
90
  return (
89
91
  <DismissibleProvider userId={userId} baseUrl="http://localhost:3001">
@@ -94,6 +96,7 @@ function App() {
94
96
  ```
95
97
 
96
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.
97
100
 
98
101
  ```tsx
99
102
  import { Dismissible } from '@dismissible/react-client';
@@ -242,11 +245,11 @@ For custom implementations and advanced use cases.
242
245
 
243
246
  | Property | Type | Description |
244
247
  |----------|------|-------------|
245
- | `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 |
246
249
  | `dismiss` | `() => Promise<void>` | Function to dismiss the item |
247
250
  | `restore` | `() => Promise<void>` | Function to restore a dismissed item |
248
251
  | `isLoading` | `boolean` | Loading state indicator |
249
- | `error` | `Error \| null` | Error state, if any |
252
+ | `error` | `Error \| undefined` | Error state, if any |
250
253
  | `item` | `IDismissibleItem \| undefined` | The full dismissible item object |
251
254
 
252
255
  #### Example
@@ -255,7 +258,7 @@ For custom implementations and advanced use cases.
255
258
  import { useDismissibleItem } from '@dismissible/react-client';
256
259
 
257
260
  function CustomDismissible({ itemId, children }) {
258
- const { dismissedOn, dismiss, restore, isLoading, error } = useDismissibleItem(itemId);
261
+ const { dismissedAt, dismiss, restore, isLoading, error } = useDismissibleItem(itemId);
259
262
 
260
263
  if (isLoading) {
261
264
  return <div>Loading...</div>;
@@ -265,7 +268,7 @@ function CustomDismissible({ itemId, children }) {
265
268
  return <div>Error: {error.message}</div>;
266
269
  }
267
270
 
268
- if (dismissedOn) {
271
+ if (dismissedAt) {
269
272
  return (
270
273
  <div>
271
274
  <p>This item was dismissed.</p>
@@ -338,7 +341,6 @@ function App() {
338
341
  function Dashboard() {
339
342
  return (
340
343
  <div>
341
- {/* Dismissible state is tracked per user */}
342
344
  <Dismissible itemId="user-welcome-banner">
343
345
  <div className="alert alert-info">
344
346
  <h4>Welcome back!</h4>
@@ -427,19 +429,19 @@ function Dashboard() {
427
429
  <div>
428
430
  <Dismissible itemId="feature-announcement">
429
431
  <div className="alert alert-success">
430
- 🎉 New feature: Dark mode is now available!
432
+ New feature: Dark mode is now available!
431
433
  </div>
432
434
  </Dismissible>
433
435
 
434
436
  <Dismissible itemId="maintenance-notice">
435
437
  <div className="alert alert-warning">
436
- ⚠️ Scheduled maintenance: Sunday 2AM-4AM EST
438
+ Scheduled maintenance: Sunday 2AM-4AM EST
437
439
  </div>
438
440
  </Dismissible>
439
441
 
440
442
  <Dismissible itemId="survey-request">
441
443
  <div className="alert alert-info">
442
- 📝 Help us improve! Take our 2-minute survey.
444
+ Help us improve! Take our 2-minute survey.
443
445
  </div>
444
446
  </Dismissible>
445
447
  </div>
@@ -468,74 +470,6 @@ function RobustBanner() {
468
470
  }
469
471
  ```
470
472
 
471
- ### Integration with Auth Providers
472
-
473
- ```tsx
474
- import { DismissibleProvider } from '@dismissible/react-client';
475
-
476
- // With Firebase Auth
477
- function AppWithFirebase() {
478
- const user = firebase.auth().currentUser;
479
-
480
- return (
481
- <DismissibleProvider
482
- userId={user.uid}
483
- jwt={async () => {
484
- if (user) {
485
- return await user.getIdToken();
486
- }
487
- throw new Error('User not authenticated');
488
- }}
489
- baseUrl="https://api.yourapp.com"
490
- >
491
- <YourApp />
492
- </DismissibleProvider>
493
- );
494
- }
495
-
496
- // With Auth0
497
- function AppWithAuth0() {
498
- const { user, getAccessTokenSilently } = useAuth0();
499
-
500
- return (
501
- <DismissibleProvider
502
- userId={user.sub}
503
- jwt={async () => await getAccessTokenSilently()}
504
- baseUrl="https://api.yourapp.com"
505
- >
506
- <YourApp />
507
- </DismissibleProvider>
508
- );
509
- }
510
-
511
- // With token refresh logic
512
- function AppWithTokenRefresh() {
513
- const { user } = useAuth();
514
-
515
- return (
516
- <DismissibleProvider
517
- userId={user.id}
518
- jwt={async () => {
519
- try {
520
- const response = await fetch('/api/auth/refresh', {
521
- method: 'POST',
522
- credentials: 'include'
523
- });
524
- const { accessToken } = await response.json();
525
- return accessToken;
526
- } catch (error) {
527
- console.error('Failed to refresh token:', error);
528
- throw error;
529
- }
530
- }}
531
- baseUrl="https://api.yourapp.com"
532
- >
533
- <YourApp />
534
- </DismissibleProvider>
535
- );
536
- }
537
- ```
538
-
539
473
  ### Using the Hook for Complex Logic
540
474
 
541
475
  ```tsx
@@ -543,12 +477,12 @@ import { useDismissibleItem } from '@dismissible/react-client';
543
477
  import { useState, useEffect } from 'react';
544
478
 
545
479
  function SmartNotification({ itemId, message, type = 'info' }) {
546
- const { dismissedOn, dismiss, isLoading } = useDismissibleItem(itemId);
480
+ const { dismissedAt, dismiss, isLoading } = useDismissibleItem(itemId);
547
481
  const [autoHide, setAutoHide] = useState(false);
548
482
 
549
483
  // Auto-hide after 10 seconds for info messages
550
484
  useEffect(() => {
551
- if (type === 'info' && !dismissedOn) {
485
+ if (type === 'info' && !dismissedAt) {
552
486
  const timer = setTimeout(() => {
553
487
  setAutoHide(true);
554
488
  dismiss();
@@ -556,9 +490,9 @@ function SmartNotification({ itemId, message, type = 'info' }) {
556
490
 
557
491
  return () => clearTimeout(timer);
558
492
  }
559
- }, [type, dismissedOn, dismiss]);
493
+ }, [type, dismissedAt, dismiss]);
560
494
 
561
- if (dismissedOn || autoHide) {
495
+ if (dismissedAt || autoHide) {
562
496
  return null;
563
497
  }
564
498
 
@@ -585,12 +519,12 @@ Use the `restore` function to bring back previously dismissed content:
585
519
  import { useDismissibleItem } from '@dismissible/react-client';
586
520
 
587
521
  function RestorableBanner({ itemId }) {
588
- const { dismissedOn, dismiss, restore, isLoading } = useDismissibleItem(itemId);
522
+ const { dismissedAt, dismiss, restore, isLoading } = useDismissibleItem(itemId);
589
523
 
590
- if (dismissedOn) {
524
+ if (dismissedAt) {
591
525
  return (
592
526
  <div className="dismissed-placeholder">
593
- <p>Banner was dismissed on {new Date(dismissedOn).toLocaleDateString()}</p>
527
+ <p>Banner was dismissed on {new Date(dismissedAt).toLocaleDateString()}</p>
594
528
  <button onClick={restore} disabled={isLoading}>
595
529
  {isLoading ? 'Restoring...' : 'Show Banner Again'}
596
530
  </button>
@@ -610,51 +544,6 @@ function RestorableBanner({ itemId }) {
610
544
  }
611
545
  ```
612
546
 
613
- ### Admin Panel with Restore Capability
614
-
615
- ```tsx
616
- import { useDismissibleItem } from '@dismissible/react-client';
617
-
618
- function AdminNotificationManager({ notificationId }) {
619
- const { dismissedOn, dismiss, restore, item, isLoading, error } =
620
- useDismissibleItem(notificationId);
621
-
622
- if (error) {
623
- return <div className="error">Error: {error.message}</div>;
624
- }
625
-
626
- return (
627
- <div className="admin-panel">
628
- <h4>Notification: {notificationId}</h4>
629
- <p>Status: {dismissedOn ? 'Dismissed' : 'Active'}</p>
630
- {dismissedOn && (
631
- <p>Dismissed at: {new Date(dismissedOn).toLocaleString()}</p>
632
- )}
633
-
634
- <div className="actions">
635
- {dismissedOn ? (
636
- <button
637
- onClick={restore}
638
- disabled={isLoading}
639
- className="btn-restore"
640
- >
641
- Restore
642
- </button>
643
- ) : (
644
- <button
645
- onClick={dismiss}
646
- disabled={isLoading}
647
- className="btn-dismiss"
648
- >
649
- Dismiss
650
- </button>
651
- )}
652
- </div>
653
- </div>
654
- );
655
- }
656
- ```
657
-
658
547
  ## Styling
659
548
 
660
549
  The library includes minimal default styles. You can override them or provide your own:
@@ -678,35 +567,6 @@ The library includes minimal default styles. You can override them or provide yo
678
567
  }
679
568
  ```
680
569
 
681
- ## TypeScript Support
682
-
683
- The library is written in TypeScript and exports all type definitions:
684
-
685
- ```tsx
686
- import type {
687
- DismissibleProps,
688
- DismissibleProviderProps,
689
- JwtToken,
690
- } from '@dismissible/react-client';
691
-
692
- // Custom provider wrapper
693
- const AuthenticatedDismissibleProvider: React.FC<{
694
- children: React.ReactNode;
695
- }> = ({ children }) => {
696
- const { user, getToken } = useAuth();
697
-
698
- return (
699
- <DismissibleProvider
700
- userId={user.id}
701
- jwt={getToken}
702
- baseUrl={process.env.DISMISSIBLE_API_URL}
703
- >
704
- {children}
705
- </DismissibleProvider>
706
- );
707
- };
708
- ```
709
-
710
570
  ## Self-Hosting
711
571
 
712
572
  Dismissible is designed to be self-hosted. You have full control over your data.
@@ -733,9 +593,9 @@ See the [NestJS documentation](https://dismissible.io/docs/nestjs) for setup ins
733
593
 
734
594
  ## Support
735
595
 
736
- - 📖 [Documentation](https://dismissible.io/docs)
737
- - 🐙 [GitHub - React Client](https://github.com/DismissibleIo/dismissible-react-client)
738
- - 🐙 [GitHub - API Server](https://github.com/DismissibleIo/dismissible-api)
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)
739
599
 
740
600
  ## License
741
601
 
@@ -1,21 +1,7 @@
1
- /**
2
- * API configuration settings
3
- */
4
- /**
5
- * Configuration interface for API settings
6
- */
7
1
  export interface ApiConfig {
8
2
  /** Base URL for API requests */
9
3
  baseUrl: string;
10
4
  }
11
- /**
12
- * Returns the API configuration based on the current environment
13
- * @returns The API configuration object
14
- */
15
5
  export declare const getConfig: () => ApiConfig;
16
- /**
17
- * Gets the API base URL
18
- * @returns The base URL for API requests
19
- */
20
6
  export declare const getBaseUrl: () => string;
21
7
  export default getConfig;