@kitbase/messaging-react 0.1.0
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 +146 -0
- package/dist/index.cjs +165 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +137 -0
- package/dist/index.d.ts +137 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @kitbase/messaging-react
|
|
2
|
+
|
|
3
|
+
React bindings for the Kitbase In-App Messaging SDK. Provides a provider and hooks for automatic or custom message rendering.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @kitbase/messaging-react
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @kitbase/messaging-react
|
|
11
|
+
# or
|
|
12
|
+
yarn add @kitbase/messaging-react
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Peer dependency: `react >= 17`
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### Auto-render (default)
|
|
20
|
+
|
|
21
|
+
Wrap your app with the provider — messages appear automatically with no extra code:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { MessagingProvider } from '@kitbase/messaging-react';
|
|
25
|
+
|
|
26
|
+
function App() {
|
|
27
|
+
return (
|
|
28
|
+
<MessagingProvider config={{ sdkKey: 'your-sdk-key', userId: user.id }}>
|
|
29
|
+
<YourApp />
|
|
30
|
+
</MessagingProvider>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Custom rendering
|
|
36
|
+
|
|
37
|
+
Set `autoShow: false` and use the `useMessages` hook to render your own UI:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { MessagingProvider, useMessages } from '@kitbase/messaging-react';
|
|
41
|
+
|
|
42
|
+
function App() {
|
|
43
|
+
return (
|
|
44
|
+
<MessagingProvider config={{ sdkKey: 'your-sdk-key', autoShow: false }}>
|
|
45
|
+
<MessageList />
|
|
46
|
+
</MessagingProvider>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function MessageList() {
|
|
51
|
+
const { messages, markViewed, isLoading } = useMessages({
|
|
52
|
+
pollInterval: 60_000,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (isLoading) return <Spinner />;
|
|
56
|
+
|
|
57
|
+
return messages.map((msg) => (
|
|
58
|
+
<div key={msg.id}>
|
|
59
|
+
<h3>{msg.title}</h3>
|
|
60
|
+
<p>{msg.body}</p>
|
|
61
|
+
{msg.actionButton && (
|
|
62
|
+
<a href={msg.actionButton.url}>{msg.actionButton.text}</a>
|
|
63
|
+
)}
|
|
64
|
+
<button onClick={() => markViewed(msg.id)}>Dismiss</button>
|
|
65
|
+
</div>
|
|
66
|
+
));
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## API
|
|
71
|
+
|
|
72
|
+
### `<MessagingProvider>`
|
|
73
|
+
|
|
74
|
+
| Prop | Type | Description |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `config` | `MessagingConfig` | SDK configuration (see [@kitbase/messaging](https://www.npmjs.com/package/@kitbase/messaging)) |
|
|
77
|
+
| `children` | `ReactNode` | Your app |
|
|
78
|
+
|
|
79
|
+
The provider creates a `Messaging` instance and cleans it up on unmount. It recreates the instance when `sdkKey` changes.
|
|
80
|
+
|
|
81
|
+
### `useMessages(options?)`
|
|
82
|
+
|
|
83
|
+
Fetches messages with optional polling. For use with `autoShow: false`.
|
|
84
|
+
|
|
85
|
+
**Options:**
|
|
86
|
+
|
|
87
|
+
| Option | Type | Default | Description |
|
|
88
|
+
|---|---|---|---|
|
|
89
|
+
| `enabled` | `boolean` | `true` | Whether to fetch on mount |
|
|
90
|
+
| `pollInterval` | `number` | — | Polling interval in ms. Omit to disable |
|
|
91
|
+
| `userId` | `string` | — | User ID for filtering |
|
|
92
|
+
| `metadata` | `Record<string, string>` | — | Targeting metadata |
|
|
93
|
+
|
|
94
|
+
**Returns:** `UseMessagesResult`
|
|
95
|
+
|
|
96
|
+
| Field | Type | Description |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `messages` | `InAppMessage[]` | Active messages |
|
|
99
|
+
| `isLoading` | `boolean` | Initial fetch in progress |
|
|
100
|
+
| `error` | `Error \| null` | Most recent fetch error |
|
|
101
|
+
| `markViewed` | `(messageId: string) => Promise<void>` | Mark as viewed and remove from list |
|
|
102
|
+
| `refresh` | `() => Promise<void>` | Manually re-fetch |
|
|
103
|
+
|
|
104
|
+
### `useLazyMessages()`
|
|
105
|
+
|
|
106
|
+
Fetch messages on demand (not on mount).
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
function InboxButton() {
|
|
110
|
+
const { fetch, messages, isLoading } = useLazyMessages();
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<button onClick={() => fetch()}>Check Messages</button>
|
|
115
|
+
{messages.map((msg) => <div key={msg.id}>{msg.title}</div>)}
|
|
116
|
+
</>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `useMessagingContext()`
|
|
122
|
+
|
|
123
|
+
Access the underlying `Messaging` instance directly for advanced usage (e.g., calling `identify()`, `reset()`).
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
const messaging = useMessagingContext();
|
|
127
|
+
messaging.identify('user_123');
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## TypeScript
|
|
131
|
+
|
|
132
|
+
All core types are re-exported for convenience:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import type {
|
|
136
|
+
MessagingConfig,
|
|
137
|
+
InAppMessage,
|
|
138
|
+
MessageType,
|
|
139
|
+
MessageButton,
|
|
140
|
+
GetMessagesOptions,
|
|
141
|
+
} from '@kitbase/messaging-react';
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
MessagingProvider: () => MessagingProvider,
|
|
24
|
+
useLazyMessages: () => useLazyMessages,
|
|
25
|
+
useMessages: () => useMessages,
|
|
26
|
+
useMessagingContext: () => useMessagingContext
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/provider.tsx
|
|
31
|
+
var import_react2 = require("react");
|
|
32
|
+
var import_messaging = require("@kitbase/messaging");
|
|
33
|
+
|
|
34
|
+
// src/context.ts
|
|
35
|
+
var import_react = require("react");
|
|
36
|
+
var MessagingContext = (0, import_react.createContext)(null);
|
|
37
|
+
function useMessagingContext() {
|
|
38
|
+
const context = (0, import_react.useContext)(MessagingContext);
|
|
39
|
+
if (!context) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
'useMessagingContext must be used within a MessagingProvider. Wrap your component with <MessagingProvider config={{ sdkKey: "..." }}>'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return context;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/provider.tsx
|
|
48
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
49
|
+
function MessagingProvider({ config, children }) {
|
|
50
|
+
const configRef = (0, import_react2.useRef)(config);
|
|
51
|
+
configRef.current = config;
|
|
52
|
+
const client = (0, import_react2.useMemo)(
|
|
53
|
+
() => new import_messaging.Messaging(config),
|
|
54
|
+
// Recreate when sdkKey changes
|
|
55
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
56
|
+
[config.sdkKey]
|
|
57
|
+
);
|
|
58
|
+
(0, import_react2.useEffect)(() => {
|
|
59
|
+
return () => {
|
|
60
|
+
client.close();
|
|
61
|
+
};
|
|
62
|
+
}, [client]);
|
|
63
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessagingContext.Provider, { value: client, children });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/use-messages.ts
|
|
67
|
+
var import_react3 = require("react");
|
|
68
|
+
function useMessages(options) {
|
|
69
|
+
const messaging = useMessagingContext();
|
|
70
|
+
const [messages, setMessages] = (0, import_react3.useState)([]);
|
|
71
|
+
const [isLoading, setIsLoading] = (0, import_react3.useState)(true);
|
|
72
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
73
|
+
const enabled = options?.enabled ?? true;
|
|
74
|
+
const pollInterval = options?.pollInterval;
|
|
75
|
+
const optionsRef = (0, import_react3.useRef)(options);
|
|
76
|
+
optionsRef.current = options;
|
|
77
|
+
const fetchMessages = (0, import_react3.useCallback)(async () => {
|
|
78
|
+
try {
|
|
79
|
+
const msgs = await messaging.getMessages(optionsRef.current);
|
|
80
|
+
setMessages(msgs);
|
|
81
|
+
setError(null);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
84
|
+
}
|
|
85
|
+
}, [messaging]);
|
|
86
|
+
(0, import_react3.useEffect)(() => {
|
|
87
|
+
if (!enabled) {
|
|
88
|
+
setIsLoading(false);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
let active = true;
|
|
92
|
+
messaging.getMessages(optionsRef.current).then((msgs) => {
|
|
93
|
+
if (active) {
|
|
94
|
+
setMessages(msgs);
|
|
95
|
+
setIsLoading(false);
|
|
96
|
+
}
|
|
97
|
+
}).catch((err) => {
|
|
98
|
+
if (active) {
|
|
99
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
100
|
+
setIsLoading(false);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
let tid;
|
|
104
|
+
if (pollInterval && pollInterval > 0) {
|
|
105
|
+
tid = setInterval(() => {
|
|
106
|
+
if (active) fetchMessages();
|
|
107
|
+
}, pollInterval);
|
|
108
|
+
}
|
|
109
|
+
return () => {
|
|
110
|
+
active = false;
|
|
111
|
+
if (tid) clearInterval(tid);
|
|
112
|
+
};
|
|
113
|
+
}, [enabled, pollInterval, fetchMessages, messaging]);
|
|
114
|
+
const markViewed = (0, import_react3.useCallback)(
|
|
115
|
+
async (messageId) => {
|
|
116
|
+
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
117
|
+
await messaging.markViewed(messageId);
|
|
118
|
+
},
|
|
119
|
+
[messaging]
|
|
120
|
+
);
|
|
121
|
+
return { messages, isLoading, error, markViewed, refresh: fetchMessages };
|
|
122
|
+
}
|
|
123
|
+
function useLazyMessages() {
|
|
124
|
+
const messaging = useMessagingContext();
|
|
125
|
+
const [messages, setMessages] = (0, import_react3.useState)([]);
|
|
126
|
+
const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
|
|
127
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
128
|
+
const fetchMessages = (0, import_react3.useCallback)(
|
|
129
|
+
async (options) => {
|
|
130
|
+
setIsLoading(true);
|
|
131
|
+
setError(null);
|
|
132
|
+
try {
|
|
133
|
+
const msgs = await messaging.getMessages(options);
|
|
134
|
+
setMessages(msgs);
|
|
135
|
+
return msgs;
|
|
136
|
+
} catch (err) {
|
|
137
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
138
|
+
return void 0;
|
|
139
|
+
} finally {
|
|
140
|
+
setIsLoading(false);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
[messaging]
|
|
144
|
+
);
|
|
145
|
+
const markViewed = (0, import_react3.useCallback)(
|
|
146
|
+
async (messageId) => {
|
|
147
|
+
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
148
|
+
await messaging.markViewed(messageId);
|
|
149
|
+
},
|
|
150
|
+
[messaging]
|
|
151
|
+
);
|
|
152
|
+
const reset = (0, import_react3.useCallback)(() => {
|
|
153
|
+
setMessages([]);
|
|
154
|
+
setError(null);
|
|
155
|
+
}, []);
|
|
156
|
+
return { fetch: fetchMessages, messages, isLoading, error, markViewed, reset };
|
|
157
|
+
}
|
|
158
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
159
|
+
0 && (module.exports = {
|
|
160
|
+
MessagingProvider,
|
|
161
|
+
useLazyMessages,
|
|
162
|
+
useMessages,
|
|
163
|
+
useMessagingContext
|
|
164
|
+
});
|
|
165
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/context.ts","../src/use-messages.ts"],"sourcesContent":["// Provider\nexport { MessagingProvider } from './provider.js';\nexport type { MessagingProviderProps } from './provider.js';\n\n// Context hook (for advanced usage)\nexport { useMessagingContext } from './context.js';\n\n// Message hooks (data-only, for custom rendering)\nexport { useMessages, useLazyMessages } from './use-messages.js';\nexport type { UseMessagesResult } from './use-messages.js';\n\n// Types\nexport type { UseMessagesOptions, AsyncState } from './types.js';\n\n// Re-export core types for convenience\nexport type {\n MessagingConfig,\n InAppMessage,\n MessageType,\n MessageButton,\n GetMessagesOptions,\n} from '@kitbase/messaging';\n","import { useMemo, useEffect, useRef, type ReactNode } from 'react';\nimport { Messaging, type MessagingConfig } from '@kitbase/messaging';\nimport { MessagingContext } from './context.js';\n\nexport interface MessagingProviderProps {\n /** Messaging SDK configuration */\n config: MessagingConfig;\n children: ReactNode;\n}\n\n/**\n * Provider for in-app messaging.\n *\n * By default, messages are fetched and rendered automatically.\n * Pass `autoShow: false` in config to use data-only hooks instead.\n *\n * @example Auto-render (default)\n * ```tsx\n * <MessagingProvider config={{ sdkKey: 'your-key', userId: user.id }}>\n * <App />\n * </MessagingProvider>\n * // Messages appear automatically — no extra code needed!\n * ```\n *\n * @example Data-only (custom rendering)\n * ```tsx\n * <MessagingProvider config={{ sdkKey: 'your-key', autoShow: false }}>\n * <App />\n * </MessagingProvider>\n * // Then use useMessages() hook to render your own UI\n * ```\n */\nexport function MessagingProvider({ config, children }: MessagingProviderProps) {\n const configRef = useRef(config);\n configRef.current = config;\n\n const client = useMemo(\n () => new Messaging(config),\n // Recreate when sdkKey changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [config.sdkKey],\n );\n\n useEffect(() => {\n return () => {\n client.close();\n };\n }, [client]);\n\n return (\n <MessagingContext.Provider value={client}>\n {children}\n </MessagingContext.Provider>\n );\n}\n","import { createContext, useContext } from 'react';\nimport type { Messaging } from '@kitbase/messaging';\n\nexport const MessagingContext = createContext<Messaging | null>(null);\n\nexport function useMessagingContext(): Messaging {\n const context = useContext(MessagingContext);\n\n if (!context) {\n throw new Error(\n 'useMessagingContext must be used within a MessagingProvider. ' +\n 'Wrap your component with <MessagingProvider config={{ sdkKey: \"...\" }}>',\n );\n }\n\n return context;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { InAppMessage, GetMessagesOptions } from '@kitbase/messaging';\nimport { useMessagingContext } from './context.js';\nimport type { UseMessagesOptions } from './types.js';\n\nexport interface UseMessagesResult {\n /** Active in-app messages */\n messages: InAppMessage[];\n /** Whether the initial fetch is in progress */\n isLoading: boolean;\n /** Error from the most recent fetch */\n error: Error | null;\n /**\n * Mark a message as viewed. Removes it from the UI immediately\n * and records the view on the server.\n * Requires `identify()` to have been called on the messaging instance.\n */\n markViewed: (messageId: string) => Promise<void>;\n /** Manually re-fetch messages */\n refresh: () => Promise<void>;\n}\n\n/**\n * Hook to fetch in-app messages (data-only, for custom rendering).\n *\n * Use this with `autoShow: false` on the provider to build your own\n * message UI. For automatic rendering, just use the default provider\n * config and skip this hook.\n *\n * @example\n * ```tsx\n * function MessageList() {\n * const { messages, markViewed, isLoading } = useMessages({\n * pollInterval: 60_000,\n * });\n *\n * if (isLoading) return <Spinner />;\n *\n * return messages.map((msg) => (\n * <div key={msg.id}>\n * <h3>{msg.title}</h3>\n * <p>{msg.body}</p>\n * {msg.actionButton && (\n * <a href={msg.actionButton.url}>{msg.actionButton.text}</a>\n * )}\n * <button onClick={() => markViewed(msg.id)}>\n * Dismiss\n * </button>\n * </div>\n * ));\n * }\n * ```\n */\nexport function useMessages(options?: UseMessagesOptions): UseMessagesResult {\n const messaging = useMessagingContext();\n const [messages, setMessages] = useState<InAppMessage[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const enabled = options?.enabled ?? true;\n const pollInterval = options?.pollInterval;\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const fetchMessages = useCallback(async () => {\n try {\n const msgs = await messaging.getMessages(optionsRef.current);\n setMessages(msgs);\n setError(null);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n }, [messaging]);\n\n useEffect(() => {\n if (!enabled) {\n setIsLoading(false);\n return;\n }\n\n let active = true;\n\n messaging\n .getMessages(optionsRef.current)\n .then((msgs) => {\n if (active) {\n setMessages(msgs);\n setIsLoading(false);\n }\n })\n .catch((err) => {\n if (active) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n });\n\n let tid: ReturnType<typeof setInterval> | undefined;\n if (pollInterval && pollInterval > 0) {\n tid = setInterval(() => {\n if (active) fetchMessages();\n }, pollInterval);\n }\n\n return () => {\n active = false;\n if (tid) clearInterval(tid);\n };\n }, [enabled, pollInterval, fetchMessages, messaging]);\n\n const markViewed = useCallback(\n async (messageId: string) => {\n setMessages((prev) => prev.filter((m) => m.id !== messageId));\n await messaging.markViewed(messageId);\n },\n [messaging],\n );\n\n return { messages, isLoading, error, markViewed, refresh: fetchMessages };\n}\n\n/**\n * Hook to lazily fetch in-app messages on demand.\n *\n * @example\n * ```tsx\n * function InboxButton() {\n * const { fetch, messages, isLoading } = useLazyMessages();\n *\n * return (\n * <>\n * <button onClick={() => fetch()}>\n * Check Messages\n * </button>\n * {messages.map((msg) => <div key={msg.id}>{msg.title}</div>)}\n * </>\n * );\n * }\n * ```\n */\nexport function useLazyMessages(): {\n fetch: (options?: GetMessagesOptions) => Promise<InAppMessage[] | undefined>;\n messages: InAppMessage[];\n isLoading: boolean;\n error: Error | null;\n markViewed: (messageId: string) => Promise<void>;\n reset: () => void;\n} {\n const messaging = useMessagingContext();\n const [messages, setMessages] = useState<InAppMessage[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchMessages = useCallback(\n async (options?: GetMessagesOptions) => {\n setIsLoading(true);\n setError(null);\n try {\n const msgs = await messaging.getMessages(options);\n setMessages(msgs);\n return msgs;\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n return undefined;\n } finally {\n setIsLoading(false);\n }\n },\n [messaging],\n );\n\n const markViewed = useCallback(\n async (messageId: string) => {\n setMessages((prev) => prev.filter((m) => m.id !== messageId));\n await messaging.markViewed(messageId);\n },\n [messaging],\n );\n\n const reset = useCallback(() => {\n setMessages([]);\n setError(null);\n }, []);\n\n return { fetch: fetchMessages, messages, isLoading, error, markViewed, reset };\n}\n"],"mappings":"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","names":["import_react","import_react"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { MessagingConfig, Messaging, GetMessagesOptions, InAppMessage } from '@kitbase/messaging';
|
|
4
|
+
export { GetMessagesOptions, InAppMessage, MessageButton, MessageType, MessagingConfig } from '@kitbase/messaging';
|
|
5
|
+
|
|
6
|
+
interface MessagingProviderProps {
|
|
7
|
+
/** Messaging SDK configuration */
|
|
8
|
+
config: MessagingConfig;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Provider for in-app messaging.
|
|
13
|
+
*
|
|
14
|
+
* By default, messages are fetched and rendered automatically.
|
|
15
|
+
* Pass `autoShow: false` in config to use data-only hooks instead.
|
|
16
|
+
*
|
|
17
|
+
* @example Auto-render (default)
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <MessagingProvider config={{ sdkKey: 'your-key', userId: user.id }}>
|
|
20
|
+
* <App />
|
|
21
|
+
* </MessagingProvider>
|
|
22
|
+
* // Messages appear automatically — no extra code needed!
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example Data-only (custom rendering)
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <MessagingProvider config={{ sdkKey: 'your-key', autoShow: false }}>
|
|
28
|
+
* <App />
|
|
29
|
+
* </MessagingProvider>
|
|
30
|
+
* // Then use useMessages() hook to render your own UI
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function MessagingProvider({ config, children }: MessagingProviderProps): react_jsx_runtime.JSX.Element;
|
|
34
|
+
|
|
35
|
+
declare function useMessagingContext(): Messaging;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Options for the useMessages hook
|
|
39
|
+
*/
|
|
40
|
+
interface UseMessagesOptions extends GetMessagesOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Whether to fetch on mount.
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Polling interval in milliseconds.
|
|
48
|
+
* Set to 0 or omit to disable polling.
|
|
49
|
+
*/
|
|
50
|
+
pollInterval?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* State for async operations
|
|
54
|
+
*/
|
|
55
|
+
interface AsyncState<T> {
|
|
56
|
+
data: T;
|
|
57
|
+
isLoading: boolean;
|
|
58
|
+
error: Error | null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface UseMessagesResult {
|
|
62
|
+
/** Active in-app messages */
|
|
63
|
+
messages: InAppMessage[];
|
|
64
|
+
/** Whether the initial fetch is in progress */
|
|
65
|
+
isLoading: boolean;
|
|
66
|
+
/** Error from the most recent fetch */
|
|
67
|
+
error: Error | null;
|
|
68
|
+
/**
|
|
69
|
+
* Mark a message as viewed. Removes it from the UI immediately
|
|
70
|
+
* and records the view on the server.
|
|
71
|
+
* Requires `identify()` to have been called on the messaging instance.
|
|
72
|
+
*/
|
|
73
|
+
markViewed: (messageId: string) => Promise<void>;
|
|
74
|
+
/** Manually re-fetch messages */
|
|
75
|
+
refresh: () => Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Hook to fetch in-app messages (data-only, for custom rendering).
|
|
79
|
+
*
|
|
80
|
+
* Use this with `autoShow: false` on the provider to build your own
|
|
81
|
+
* message UI. For automatic rendering, just use the default provider
|
|
82
|
+
* config and skip this hook.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* function MessageList() {
|
|
87
|
+
* const { messages, markViewed, isLoading } = useMessages({
|
|
88
|
+
* pollInterval: 60_000,
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* if (isLoading) return <Spinner />;
|
|
92
|
+
*
|
|
93
|
+
* return messages.map((msg) => (
|
|
94
|
+
* <div key={msg.id}>
|
|
95
|
+
* <h3>{msg.title}</h3>
|
|
96
|
+
* <p>{msg.body}</p>
|
|
97
|
+
* {msg.actionButton && (
|
|
98
|
+
* <a href={msg.actionButton.url}>{msg.actionButton.text}</a>
|
|
99
|
+
* )}
|
|
100
|
+
* <button onClick={() => markViewed(msg.id)}>
|
|
101
|
+
* Dismiss
|
|
102
|
+
* </button>
|
|
103
|
+
* </div>
|
|
104
|
+
* ));
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare function useMessages(options?: UseMessagesOptions): UseMessagesResult;
|
|
109
|
+
/**
|
|
110
|
+
* Hook to lazily fetch in-app messages on demand.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```tsx
|
|
114
|
+
* function InboxButton() {
|
|
115
|
+
* const { fetch, messages, isLoading } = useLazyMessages();
|
|
116
|
+
*
|
|
117
|
+
* return (
|
|
118
|
+
* <>
|
|
119
|
+
* <button onClick={() => fetch()}>
|
|
120
|
+
* Check Messages
|
|
121
|
+
* </button>
|
|
122
|
+
* {messages.map((msg) => <div key={msg.id}>{msg.title}</div>)}
|
|
123
|
+
* </>
|
|
124
|
+
* );
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
declare function useLazyMessages(): {
|
|
129
|
+
fetch: (options?: GetMessagesOptions) => Promise<InAppMessage[] | undefined>;
|
|
130
|
+
messages: InAppMessage[];
|
|
131
|
+
isLoading: boolean;
|
|
132
|
+
error: Error | null;
|
|
133
|
+
markViewed: (messageId: string) => Promise<void>;
|
|
134
|
+
reset: () => void;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export { type AsyncState, MessagingProvider, type MessagingProviderProps, type UseMessagesOptions, type UseMessagesResult, useLazyMessages, useMessages, useMessagingContext };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { MessagingConfig, Messaging, GetMessagesOptions, InAppMessage } from '@kitbase/messaging';
|
|
4
|
+
export { GetMessagesOptions, InAppMessage, MessageButton, MessageType, MessagingConfig } from '@kitbase/messaging';
|
|
5
|
+
|
|
6
|
+
interface MessagingProviderProps {
|
|
7
|
+
/** Messaging SDK configuration */
|
|
8
|
+
config: MessagingConfig;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Provider for in-app messaging.
|
|
13
|
+
*
|
|
14
|
+
* By default, messages are fetched and rendered automatically.
|
|
15
|
+
* Pass `autoShow: false` in config to use data-only hooks instead.
|
|
16
|
+
*
|
|
17
|
+
* @example Auto-render (default)
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <MessagingProvider config={{ sdkKey: 'your-key', userId: user.id }}>
|
|
20
|
+
* <App />
|
|
21
|
+
* </MessagingProvider>
|
|
22
|
+
* // Messages appear automatically — no extra code needed!
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example Data-only (custom rendering)
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <MessagingProvider config={{ sdkKey: 'your-key', autoShow: false }}>
|
|
28
|
+
* <App />
|
|
29
|
+
* </MessagingProvider>
|
|
30
|
+
* // Then use useMessages() hook to render your own UI
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function MessagingProvider({ config, children }: MessagingProviderProps): react_jsx_runtime.JSX.Element;
|
|
34
|
+
|
|
35
|
+
declare function useMessagingContext(): Messaging;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Options for the useMessages hook
|
|
39
|
+
*/
|
|
40
|
+
interface UseMessagesOptions extends GetMessagesOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Whether to fetch on mount.
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Polling interval in milliseconds.
|
|
48
|
+
* Set to 0 or omit to disable polling.
|
|
49
|
+
*/
|
|
50
|
+
pollInterval?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* State for async operations
|
|
54
|
+
*/
|
|
55
|
+
interface AsyncState<T> {
|
|
56
|
+
data: T;
|
|
57
|
+
isLoading: boolean;
|
|
58
|
+
error: Error | null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface UseMessagesResult {
|
|
62
|
+
/** Active in-app messages */
|
|
63
|
+
messages: InAppMessage[];
|
|
64
|
+
/** Whether the initial fetch is in progress */
|
|
65
|
+
isLoading: boolean;
|
|
66
|
+
/** Error from the most recent fetch */
|
|
67
|
+
error: Error | null;
|
|
68
|
+
/**
|
|
69
|
+
* Mark a message as viewed. Removes it from the UI immediately
|
|
70
|
+
* and records the view on the server.
|
|
71
|
+
* Requires `identify()` to have been called on the messaging instance.
|
|
72
|
+
*/
|
|
73
|
+
markViewed: (messageId: string) => Promise<void>;
|
|
74
|
+
/** Manually re-fetch messages */
|
|
75
|
+
refresh: () => Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Hook to fetch in-app messages (data-only, for custom rendering).
|
|
79
|
+
*
|
|
80
|
+
* Use this with `autoShow: false` on the provider to build your own
|
|
81
|
+
* message UI. For automatic rendering, just use the default provider
|
|
82
|
+
* config and skip this hook.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* function MessageList() {
|
|
87
|
+
* const { messages, markViewed, isLoading } = useMessages({
|
|
88
|
+
* pollInterval: 60_000,
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* if (isLoading) return <Spinner />;
|
|
92
|
+
*
|
|
93
|
+
* return messages.map((msg) => (
|
|
94
|
+
* <div key={msg.id}>
|
|
95
|
+
* <h3>{msg.title}</h3>
|
|
96
|
+
* <p>{msg.body}</p>
|
|
97
|
+
* {msg.actionButton && (
|
|
98
|
+
* <a href={msg.actionButton.url}>{msg.actionButton.text}</a>
|
|
99
|
+
* )}
|
|
100
|
+
* <button onClick={() => markViewed(msg.id)}>
|
|
101
|
+
* Dismiss
|
|
102
|
+
* </button>
|
|
103
|
+
* </div>
|
|
104
|
+
* ));
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare function useMessages(options?: UseMessagesOptions): UseMessagesResult;
|
|
109
|
+
/**
|
|
110
|
+
* Hook to lazily fetch in-app messages on demand.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```tsx
|
|
114
|
+
* function InboxButton() {
|
|
115
|
+
* const { fetch, messages, isLoading } = useLazyMessages();
|
|
116
|
+
*
|
|
117
|
+
* return (
|
|
118
|
+
* <>
|
|
119
|
+
* <button onClick={() => fetch()}>
|
|
120
|
+
* Check Messages
|
|
121
|
+
* </button>
|
|
122
|
+
* {messages.map((msg) => <div key={msg.id}>{msg.title}</div>)}
|
|
123
|
+
* </>
|
|
124
|
+
* );
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
declare function useLazyMessages(): {
|
|
129
|
+
fetch: (options?: GetMessagesOptions) => Promise<InAppMessage[] | undefined>;
|
|
130
|
+
messages: InAppMessage[];
|
|
131
|
+
isLoading: boolean;
|
|
132
|
+
error: Error | null;
|
|
133
|
+
markViewed: (messageId: string) => Promise<void>;
|
|
134
|
+
reset: () => void;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export { type AsyncState, MessagingProvider, type MessagingProviderProps, type UseMessagesOptions, type UseMessagesResult, useLazyMessages, useMessages, useMessagingContext };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// src/provider.tsx
|
|
2
|
+
import { useMemo, useEffect, useRef } from "react";
|
|
3
|
+
import { Messaging } from "@kitbase/messaging";
|
|
4
|
+
|
|
5
|
+
// src/context.ts
|
|
6
|
+
import { createContext, useContext } from "react";
|
|
7
|
+
var MessagingContext = createContext(null);
|
|
8
|
+
function useMessagingContext() {
|
|
9
|
+
const context = useContext(MessagingContext);
|
|
10
|
+
if (!context) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
'useMessagingContext must be used within a MessagingProvider. Wrap your component with <MessagingProvider config={{ sdkKey: "..." }}>'
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/provider.tsx
|
|
19
|
+
import { jsx } from "react/jsx-runtime";
|
|
20
|
+
function MessagingProvider({ config, children }) {
|
|
21
|
+
const configRef = useRef(config);
|
|
22
|
+
configRef.current = config;
|
|
23
|
+
const client = useMemo(
|
|
24
|
+
() => new Messaging(config),
|
|
25
|
+
// Recreate when sdkKey changes
|
|
26
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
27
|
+
[config.sdkKey]
|
|
28
|
+
);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
return () => {
|
|
31
|
+
client.close();
|
|
32
|
+
};
|
|
33
|
+
}, [client]);
|
|
34
|
+
return /* @__PURE__ */ jsx(MessagingContext.Provider, { value: client, children });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/use-messages.ts
|
|
38
|
+
import { useState, useEffect as useEffect2, useCallback, useRef as useRef2 } from "react";
|
|
39
|
+
function useMessages(options) {
|
|
40
|
+
const messaging = useMessagingContext();
|
|
41
|
+
const [messages, setMessages] = useState([]);
|
|
42
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
43
|
+
const [error, setError] = useState(null);
|
|
44
|
+
const enabled = options?.enabled ?? true;
|
|
45
|
+
const pollInterval = options?.pollInterval;
|
|
46
|
+
const optionsRef = useRef2(options);
|
|
47
|
+
optionsRef.current = options;
|
|
48
|
+
const fetchMessages = useCallback(async () => {
|
|
49
|
+
try {
|
|
50
|
+
const msgs = await messaging.getMessages(optionsRef.current);
|
|
51
|
+
setMessages(msgs);
|
|
52
|
+
setError(null);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
55
|
+
}
|
|
56
|
+
}, [messaging]);
|
|
57
|
+
useEffect2(() => {
|
|
58
|
+
if (!enabled) {
|
|
59
|
+
setIsLoading(false);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let active = true;
|
|
63
|
+
messaging.getMessages(optionsRef.current).then((msgs) => {
|
|
64
|
+
if (active) {
|
|
65
|
+
setMessages(msgs);
|
|
66
|
+
setIsLoading(false);
|
|
67
|
+
}
|
|
68
|
+
}).catch((err) => {
|
|
69
|
+
if (active) {
|
|
70
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
71
|
+
setIsLoading(false);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
let tid;
|
|
75
|
+
if (pollInterval && pollInterval > 0) {
|
|
76
|
+
tid = setInterval(() => {
|
|
77
|
+
if (active) fetchMessages();
|
|
78
|
+
}, pollInterval);
|
|
79
|
+
}
|
|
80
|
+
return () => {
|
|
81
|
+
active = false;
|
|
82
|
+
if (tid) clearInterval(tid);
|
|
83
|
+
};
|
|
84
|
+
}, [enabled, pollInterval, fetchMessages, messaging]);
|
|
85
|
+
const markViewed = useCallback(
|
|
86
|
+
async (messageId) => {
|
|
87
|
+
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
88
|
+
await messaging.markViewed(messageId);
|
|
89
|
+
},
|
|
90
|
+
[messaging]
|
|
91
|
+
);
|
|
92
|
+
return { messages, isLoading, error, markViewed, refresh: fetchMessages };
|
|
93
|
+
}
|
|
94
|
+
function useLazyMessages() {
|
|
95
|
+
const messaging = useMessagingContext();
|
|
96
|
+
const [messages, setMessages] = useState([]);
|
|
97
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
98
|
+
const [error, setError] = useState(null);
|
|
99
|
+
const fetchMessages = useCallback(
|
|
100
|
+
async (options) => {
|
|
101
|
+
setIsLoading(true);
|
|
102
|
+
setError(null);
|
|
103
|
+
try {
|
|
104
|
+
const msgs = await messaging.getMessages(options);
|
|
105
|
+
setMessages(msgs);
|
|
106
|
+
return msgs;
|
|
107
|
+
} catch (err) {
|
|
108
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
109
|
+
return void 0;
|
|
110
|
+
} finally {
|
|
111
|
+
setIsLoading(false);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
[messaging]
|
|
115
|
+
);
|
|
116
|
+
const markViewed = useCallback(
|
|
117
|
+
async (messageId) => {
|
|
118
|
+
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
119
|
+
await messaging.markViewed(messageId);
|
|
120
|
+
},
|
|
121
|
+
[messaging]
|
|
122
|
+
);
|
|
123
|
+
const reset = useCallback(() => {
|
|
124
|
+
setMessages([]);
|
|
125
|
+
setError(null);
|
|
126
|
+
}, []);
|
|
127
|
+
return { fetch: fetchMessages, messages, isLoading, error, markViewed, reset };
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
MessagingProvider,
|
|
131
|
+
useLazyMessages,
|
|
132
|
+
useMessages,
|
|
133
|
+
useMessagingContext
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx","../src/context.ts","../src/use-messages.ts"],"sourcesContent":["import { useMemo, useEffect, useRef, type ReactNode } from 'react';\nimport { Messaging, type MessagingConfig } from '@kitbase/messaging';\nimport { MessagingContext } from './context.js';\n\nexport interface MessagingProviderProps {\n /** Messaging SDK configuration */\n config: MessagingConfig;\n children: ReactNode;\n}\n\n/**\n * Provider for in-app messaging.\n *\n * By default, messages are fetched and rendered automatically.\n * Pass `autoShow: false` in config to use data-only hooks instead.\n *\n * @example Auto-render (default)\n * ```tsx\n * <MessagingProvider config={{ sdkKey: 'your-key', userId: user.id }}>\n * <App />\n * </MessagingProvider>\n * // Messages appear automatically — no extra code needed!\n * ```\n *\n * @example Data-only (custom rendering)\n * ```tsx\n * <MessagingProvider config={{ sdkKey: 'your-key', autoShow: false }}>\n * <App />\n * </MessagingProvider>\n * // Then use useMessages() hook to render your own UI\n * ```\n */\nexport function MessagingProvider({ config, children }: MessagingProviderProps) {\n const configRef = useRef(config);\n configRef.current = config;\n\n const client = useMemo(\n () => new Messaging(config),\n // Recreate when sdkKey changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [config.sdkKey],\n );\n\n useEffect(() => {\n return () => {\n client.close();\n };\n }, [client]);\n\n return (\n <MessagingContext.Provider value={client}>\n {children}\n </MessagingContext.Provider>\n );\n}\n","import { createContext, useContext } from 'react';\nimport type { Messaging } from '@kitbase/messaging';\n\nexport const MessagingContext = createContext<Messaging | null>(null);\n\nexport function useMessagingContext(): Messaging {\n const context = useContext(MessagingContext);\n\n if (!context) {\n throw new Error(\n 'useMessagingContext must be used within a MessagingProvider. ' +\n 'Wrap your component with <MessagingProvider config={{ sdkKey: \"...\" }}>',\n );\n }\n\n return context;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { InAppMessage, GetMessagesOptions } from '@kitbase/messaging';\nimport { useMessagingContext } from './context.js';\nimport type { UseMessagesOptions } from './types.js';\n\nexport interface UseMessagesResult {\n /** Active in-app messages */\n messages: InAppMessage[];\n /** Whether the initial fetch is in progress */\n isLoading: boolean;\n /** Error from the most recent fetch */\n error: Error | null;\n /**\n * Mark a message as viewed. Removes it from the UI immediately\n * and records the view on the server.\n * Requires `identify()` to have been called on the messaging instance.\n */\n markViewed: (messageId: string) => Promise<void>;\n /** Manually re-fetch messages */\n refresh: () => Promise<void>;\n}\n\n/**\n * Hook to fetch in-app messages (data-only, for custom rendering).\n *\n * Use this with `autoShow: false` on the provider to build your own\n * message UI. For automatic rendering, just use the default provider\n * config and skip this hook.\n *\n * @example\n * ```tsx\n * function MessageList() {\n * const { messages, markViewed, isLoading } = useMessages({\n * pollInterval: 60_000,\n * });\n *\n * if (isLoading) return <Spinner />;\n *\n * return messages.map((msg) => (\n * <div key={msg.id}>\n * <h3>{msg.title}</h3>\n * <p>{msg.body}</p>\n * {msg.actionButton && (\n * <a href={msg.actionButton.url}>{msg.actionButton.text}</a>\n * )}\n * <button onClick={() => markViewed(msg.id)}>\n * Dismiss\n * </button>\n * </div>\n * ));\n * }\n * ```\n */\nexport function useMessages(options?: UseMessagesOptions): UseMessagesResult {\n const messaging = useMessagingContext();\n const [messages, setMessages] = useState<InAppMessage[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const enabled = options?.enabled ?? true;\n const pollInterval = options?.pollInterval;\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const fetchMessages = useCallback(async () => {\n try {\n const msgs = await messaging.getMessages(optionsRef.current);\n setMessages(msgs);\n setError(null);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n }, [messaging]);\n\n useEffect(() => {\n if (!enabled) {\n setIsLoading(false);\n return;\n }\n\n let active = true;\n\n messaging\n .getMessages(optionsRef.current)\n .then((msgs) => {\n if (active) {\n setMessages(msgs);\n setIsLoading(false);\n }\n })\n .catch((err) => {\n if (active) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n });\n\n let tid: ReturnType<typeof setInterval> | undefined;\n if (pollInterval && pollInterval > 0) {\n tid = setInterval(() => {\n if (active) fetchMessages();\n }, pollInterval);\n }\n\n return () => {\n active = false;\n if (tid) clearInterval(tid);\n };\n }, [enabled, pollInterval, fetchMessages, messaging]);\n\n const markViewed = useCallback(\n async (messageId: string) => {\n setMessages((prev) => prev.filter((m) => m.id !== messageId));\n await messaging.markViewed(messageId);\n },\n [messaging],\n );\n\n return { messages, isLoading, error, markViewed, refresh: fetchMessages };\n}\n\n/**\n * Hook to lazily fetch in-app messages on demand.\n *\n * @example\n * ```tsx\n * function InboxButton() {\n * const { fetch, messages, isLoading } = useLazyMessages();\n *\n * return (\n * <>\n * <button onClick={() => fetch()}>\n * Check Messages\n * </button>\n * {messages.map((msg) => <div key={msg.id}>{msg.title}</div>)}\n * </>\n * );\n * }\n * ```\n */\nexport function useLazyMessages(): {\n fetch: (options?: GetMessagesOptions) => Promise<InAppMessage[] | undefined>;\n messages: InAppMessage[];\n isLoading: boolean;\n error: Error | null;\n markViewed: (messageId: string) => Promise<void>;\n reset: () => void;\n} {\n const messaging = useMessagingContext();\n const [messages, setMessages] = useState<InAppMessage[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchMessages = useCallback(\n async (options?: GetMessagesOptions) => {\n setIsLoading(true);\n setError(null);\n try {\n const msgs = await messaging.getMessages(options);\n setMessages(msgs);\n return msgs;\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n return undefined;\n } finally {\n setIsLoading(false);\n }\n },\n [messaging],\n );\n\n const markViewed = useCallback(\n async (messageId: string) => {\n setMessages((prev) => prev.filter((m) => m.id !== messageId));\n await messaging.markViewed(messageId);\n },\n [messaging],\n );\n\n const reset = useCallback(() => {\n setMessages([]);\n setError(null);\n }, []);\n\n return { fetch: fetchMessages, messages, isLoading, error, markViewed, reset };\n}\n"],"mappings":"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","names":["useEffect","useRef","useRef","useEffect"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kitbase/messaging-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Kitbase In-App Messaging React SDK - React hooks and providers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"keywords": [
|
|
26
|
+
"kitbase",
|
|
27
|
+
"messaging",
|
|
28
|
+
"in-app-messages",
|
|
29
|
+
"react",
|
|
30
|
+
"hooks"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/scr2em/kitbase-sdk.git",
|
|
36
|
+
"directory": "messaging/react"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": ">=17.0.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@kitbase/messaging": "0.1.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@testing-library/react": "^14.1.2",
|
|
46
|
+
"@types/react": "^18.2.48",
|
|
47
|
+
"jsdom": "^23.2.0",
|
|
48
|
+
"react": "^18.2.0",
|
|
49
|
+
"tsup": "^8.0.1",
|
|
50
|
+
"typescript": "^5.3.3",
|
|
51
|
+
"vitest": "^1.2.0"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"clean": "rm -rf dist"
|
|
60
|
+
}
|
|
61
|
+
}
|