@channlize/react 0.3.0 → 0.4.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/CHANGELOG.md +21 -0
- package/README.md +20 -3
- package/dist/index.d.mts +10 -6
- package/dist/index.d.ts +10 -6
- package/dist/index.js +77 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +86 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -9
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/context/ChannlizeContext.tsx
|
|
2
|
-
import { createContext, useContext } from "react";
|
|
2
|
+
import { createContext, useContext, useMemo } from "react";
|
|
3
3
|
import { ChannlizeClient } from "@channlize/core";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
var ChannlizeContext = createContext(null);
|
|
@@ -7,8 +7,19 @@ function ChannlizeProvider({
|
|
|
7
7
|
config,
|
|
8
8
|
children
|
|
9
9
|
}) {
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const { apiKey, baseUrl, projectId, timeout, serverApiKey } = config;
|
|
11
|
+
const client = useMemo(
|
|
12
|
+
() => new ChannlizeClient({
|
|
13
|
+
apiKey,
|
|
14
|
+
baseUrl,
|
|
15
|
+
projectId,
|
|
16
|
+
timeout,
|
|
17
|
+
serverApiKey
|
|
18
|
+
}),
|
|
19
|
+
[apiKey, baseUrl, projectId, timeout, serverApiKey]
|
|
20
|
+
);
|
|
21
|
+
const value = useMemo(() => ({ client, config }), [client, config]);
|
|
22
|
+
return /* @__PURE__ */ jsx(ChannlizeContext.Provider, { value, children });
|
|
12
23
|
}
|
|
13
24
|
function useChannlize() {
|
|
14
25
|
const context = useContext(ChannlizeContext);
|
|
@@ -60,6 +71,7 @@ function useContactForm(options = {}) {
|
|
|
60
71
|
|
|
61
72
|
// src/hooks/useChatSession.ts
|
|
62
73
|
import { useState as useState2, useEffect, useCallback as useCallback2, useRef } from "react";
|
|
74
|
+
import { DEFAULT_BASE_URL } from "@channlize/core";
|
|
63
75
|
function useChatSession(options = {}) {
|
|
64
76
|
const { client, config } = useChannlize();
|
|
65
77
|
const [session, setSession] = useState2(null);
|
|
@@ -70,9 +82,27 @@ function useChatSession(options = {}) {
|
|
|
70
82
|
const eventSourceRef = useRef(null);
|
|
71
83
|
const sessionRef = useRef(null);
|
|
72
84
|
const isInitializedRef = useRef(false);
|
|
73
|
-
const baseUrl = config.baseUrl ||
|
|
85
|
+
const baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
74
86
|
const storageKey = options.storageKey || `channlize_session_${config.apiKey}`;
|
|
75
87
|
const persistSession = options.persistSession ?? true;
|
|
88
|
+
const latest = useRef({
|
|
89
|
+
onMessage: options.onMessage,
|
|
90
|
+
onError: options.onError,
|
|
91
|
+
debug: options.debug
|
|
92
|
+
});
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
latest.current = {
|
|
95
|
+
onMessage: options.onMessage,
|
|
96
|
+
onError: options.onError,
|
|
97
|
+
debug: options.debug
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
const log = useCallback2(
|
|
101
|
+
(level, ...args) => {
|
|
102
|
+
if (latest.current.debug) console[level]("[channlize]", ...args);
|
|
103
|
+
},
|
|
104
|
+
[]
|
|
105
|
+
);
|
|
76
106
|
useEffect(() => {
|
|
77
107
|
sessionRef.current = session;
|
|
78
108
|
}, [session]);
|
|
@@ -82,51 +112,50 @@ function useChatSession(options = {}) {
|
|
|
82
112
|
try {
|
|
83
113
|
localStorage.setItem(storageKey, sessionId);
|
|
84
114
|
} catch (err) {
|
|
85
|
-
|
|
115
|
+
log("warn", "Failed to save session to localStorage:", err);
|
|
86
116
|
}
|
|
87
117
|
}
|
|
88
118
|
},
|
|
89
|
-
[persistSession, storageKey]
|
|
119
|
+
[persistSession, storageKey, log]
|
|
90
120
|
);
|
|
91
121
|
const getSessionFromStorage = useCallback2(() => {
|
|
92
122
|
if (persistSession && typeof window !== "undefined") {
|
|
93
123
|
try {
|
|
94
124
|
return localStorage.getItem(storageKey);
|
|
95
125
|
} catch (err) {
|
|
96
|
-
|
|
126
|
+
log("warn", "Failed to get session from localStorage:", err);
|
|
97
127
|
}
|
|
98
128
|
}
|
|
99
129
|
return null;
|
|
100
|
-
}, [persistSession, storageKey]);
|
|
130
|
+
}, [persistSession, storageKey, log]);
|
|
101
131
|
const clearSessionFromStorage = useCallback2(() => {
|
|
102
132
|
if (persistSession && typeof window !== "undefined") {
|
|
103
133
|
try {
|
|
104
134
|
localStorage.removeItem(storageKey);
|
|
105
135
|
} catch (err) {
|
|
106
|
-
|
|
136
|
+
log("warn", "Failed to clear session from localStorage:", err);
|
|
107
137
|
}
|
|
108
138
|
}
|
|
109
|
-
}, [persistSession, storageKey]);
|
|
139
|
+
}, [persistSession, storageKey, log]);
|
|
110
140
|
const connectToSSE = useCallback2(
|
|
111
141
|
(sseEndpoint) => {
|
|
112
142
|
if (eventSourceRef.current) {
|
|
113
143
|
eventSourceRef.current.close();
|
|
114
144
|
}
|
|
115
145
|
const fullUrl = sseEndpoint.startsWith("http") ? sseEndpoint : `${baseUrl}${sseEndpoint}`;
|
|
116
|
-
|
|
146
|
+
log("log", "SSE connecting to", fullUrl);
|
|
117
147
|
const eventSource = new EventSource(fullUrl);
|
|
118
148
|
eventSourceRef.current = eventSource;
|
|
119
149
|
eventSource.onopen = () => {
|
|
120
|
-
|
|
150
|
+
log("log", "SSE connection opened");
|
|
121
151
|
setIsConnected(true);
|
|
122
152
|
setError(null);
|
|
123
153
|
};
|
|
124
154
|
eventSource.addEventListener("connected", (event) => {
|
|
125
|
-
|
|
155
|
+
log("log", "SSE connected:", event.data);
|
|
126
156
|
});
|
|
127
157
|
eventSource.addEventListener("message", (event) => {
|
|
128
158
|
try {
|
|
129
|
-
console.log("SSE message:", event.data);
|
|
130
159
|
const message = JSON.parse(event.data);
|
|
131
160
|
const chatMessage = {
|
|
132
161
|
...message,
|
|
@@ -136,9 +165,9 @@ function useChatSession(options = {}) {
|
|
|
136
165
|
const exists = prev.some((m) => m.id === chatMessage.id);
|
|
137
166
|
return exists ? prev : [...prev, chatMessage];
|
|
138
167
|
});
|
|
139
|
-
|
|
168
|
+
latest.current.onMessage?.(chatMessage);
|
|
140
169
|
} catch (err) {
|
|
141
|
-
|
|
170
|
+
log("warn", "Failed to parse SSE message:", err, event.data);
|
|
142
171
|
}
|
|
143
172
|
});
|
|
144
173
|
eventSource.onmessage = (event) => {
|
|
@@ -153,10 +182,10 @@ function useChatSession(options = {}) {
|
|
|
153
182
|
const exists = prev.some((m) => m.id === message.id);
|
|
154
183
|
return exists ? prev : [...prev, message];
|
|
155
184
|
});
|
|
156
|
-
|
|
185
|
+
latest.current.onMessage?.(message);
|
|
157
186
|
}
|
|
158
187
|
} catch (err) {
|
|
159
|
-
|
|
188
|
+
log("warn", "Failed to parse default SSE message:", err);
|
|
160
189
|
}
|
|
161
190
|
};
|
|
162
191
|
eventSource.onerror = (event) => {
|
|
@@ -164,7 +193,7 @@ function useChatSession(options = {}) {
|
|
|
164
193
|
setIsConnected(false);
|
|
165
194
|
return;
|
|
166
195
|
}
|
|
167
|
-
|
|
196
|
+
log("error", "SSE connection error", {
|
|
168
197
|
readyState: eventSource.readyState,
|
|
169
198
|
url: fullUrl,
|
|
170
199
|
event
|
|
@@ -176,10 +205,10 @@ function useChatSession(options = {}) {
|
|
|
176
205
|
const error2 = new Error(`SSE ${stateMessage}`);
|
|
177
206
|
error2.code = "SSE_ERROR";
|
|
178
207
|
setError(error2);
|
|
179
|
-
|
|
208
|
+
latest.current.onError?.(error2);
|
|
180
209
|
};
|
|
181
210
|
},
|
|
182
|
-
[baseUrl,
|
|
211
|
+
[baseUrl, log]
|
|
183
212
|
);
|
|
184
213
|
const startSession = useCallback2(async () => {
|
|
185
214
|
if (isConnecting || session) return;
|
|
@@ -189,7 +218,7 @@ function useChatSession(options = {}) {
|
|
|
189
218
|
const existingSessionId = options.sessionId || getSessionFromStorage();
|
|
190
219
|
if (existingSessionId) {
|
|
191
220
|
try {
|
|
192
|
-
|
|
221
|
+
log("log", "Resuming session", existingSessionId);
|
|
193
222
|
const existingSession = await client.getChatSession(existingSessionId);
|
|
194
223
|
const existingMessages = await client.getChatMessages(existingSessionId);
|
|
195
224
|
sessionRef.current = existingSession;
|
|
@@ -203,14 +232,14 @@ function useChatSession(options = {}) {
|
|
|
203
232
|
setMessages(normalizedMessages2);
|
|
204
233
|
const sseEndpoint = `/v1/chat/sessions/${existingSessionId}/events`;
|
|
205
234
|
connectToSSE(sseEndpoint);
|
|
206
|
-
|
|
235
|
+
log("log", "Session resumed");
|
|
207
236
|
return;
|
|
208
237
|
} catch (err) {
|
|
209
|
-
|
|
238
|
+
log("warn", "Could not resume the session, starting a new one:", err);
|
|
210
239
|
clearSessionFromStorage();
|
|
211
240
|
}
|
|
212
241
|
}
|
|
213
|
-
|
|
242
|
+
log("log", "Creating a chat session");
|
|
214
243
|
const response = await client.createChatSession(options.metadata);
|
|
215
244
|
sessionRef.current = response.session;
|
|
216
245
|
setSession(response.session);
|
|
@@ -225,19 +254,19 @@ function useChatSession(options = {}) {
|
|
|
225
254
|
if (response.sseEndpoint) {
|
|
226
255
|
connectToSSE(response.sseEndpoint);
|
|
227
256
|
}
|
|
228
|
-
|
|
257
|
+
log("log", "Session created", response.session.id);
|
|
229
258
|
} catch (err) {
|
|
230
259
|
const error2 = err;
|
|
231
260
|
setError(error2);
|
|
232
|
-
|
|
261
|
+
latest.current.onError?.(error2);
|
|
233
262
|
} finally {
|
|
234
263
|
setIsConnecting(false);
|
|
235
264
|
}
|
|
236
265
|
}, [
|
|
237
266
|
client,
|
|
238
267
|
connectToSSE,
|
|
268
|
+
log,
|
|
239
269
|
options.metadata,
|
|
240
|
-
options.onError,
|
|
241
270
|
options.sessionId,
|
|
242
271
|
getSessionFromStorage,
|
|
243
272
|
saveSessionToStorage,
|
|
@@ -268,11 +297,11 @@ function useChatSession(options = {}) {
|
|
|
268
297
|
} catch (err) {
|
|
269
298
|
const error2 = err;
|
|
270
299
|
setError(error2);
|
|
271
|
-
|
|
300
|
+
latest.current.onError?.(error2);
|
|
272
301
|
throw error2;
|
|
273
302
|
}
|
|
274
303
|
},
|
|
275
|
-
[client
|
|
304
|
+
[client]
|
|
276
305
|
);
|
|
277
306
|
const endSession = useCallback2(async () => {
|
|
278
307
|
if (!sessionRef.current) return;
|
|
@@ -287,13 +316,13 @@ function useChatSession(options = {}) {
|
|
|
287
316
|
setSession(null);
|
|
288
317
|
setMessages([]);
|
|
289
318
|
setIsConnected(false);
|
|
290
|
-
|
|
319
|
+
log("log", "Session ended");
|
|
291
320
|
} catch (err) {
|
|
292
321
|
const error2 = err;
|
|
293
322
|
setError(error2);
|
|
294
|
-
|
|
323
|
+
latest.current.onError?.(error2);
|
|
295
324
|
}
|
|
296
|
-
}, [client,
|
|
325
|
+
}, [client, log, clearSessionFromStorage]);
|
|
297
326
|
const reconnect = useCallback2(() => {
|
|
298
327
|
const current = sessionRef.current;
|
|
299
328
|
if (!current) return;
|
|
@@ -344,7 +373,7 @@ function useChannlizeAnalytics() {
|
|
|
344
373
|
// src/components/Form.tsx
|
|
345
374
|
import {
|
|
346
375
|
useEffect as useEffect2,
|
|
347
|
-
useMemo,
|
|
376
|
+
useMemo as useMemo2,
|
|
348
377
|
useRef as useRef2,
|
|
349
378
|
useState as useState3
|
|
350
379
|
} from "react";
|
|
@@ -787,7 +816,7 @@ function Form({
|
|
|
787
816
|
...submitText !== void 0 && { submit: submitText },
|
|
788
817
|
...successMessage !== void 0 && { successMessage }
|
|
789
818
|
};
|
|
790
|
-
const topicOptions =
|
|
819
|
+
const topicOptions = useMemo2(() => normalizeTopics(topics), [topics]);
|
|
791
820
|
const modes = {
|
|
792
821
|
name: fields.name ?? "required",
|
|
793
822
|
company: fields.company ?? "hidden",
|
|
@@ -1284,10 +1313,11 @@ import {
|
|
|
1284
1313
|
useCallback as useCallback4,
|
|
1285
1314
|
useEffect as useEffect4,
|
|
1286
1315
|
useId,
|
|
1287
|
-
useMemo as
|
|
1316
|
+
useMemo as useMemo3,
|
|
1288
1317
|
useRef as useRef3,
|
|
1289
1318
|
useState as useState5
|
|
1290
1319
|
} from "react";
|
|
1320
|
+
import { DEFAULT_BASE_URL as DEFAULT_BASE_URL2 } from "@channlize/core";
|
|
1291
1321
|
|
|
1292
1322
|
// src/components/chat/internal.tsx
|
|
1293
1323
|
import {
|
|
@@ -1650,6 +1680,7 @@ function SupportChat({
|
|
|
1650
1680
|
sessionId,
|
|
1651
1681
|
persistSession,
|
|
1652
1682
|
storageKey,
|
|
1683
|
+
debug,
|
|
1653
1684
|
className = "",
|
|
1654
1685
|
styles = {},
|
|
1655
1686
|
placeholder = "Write a message\u2026",
|
|
@@ -1672,12 +1703,12 @@ function SupportChat({
|
|
|
1672
1703
|
onMinimize,
|
|
1673
1704
|
labels: labelOverrides
|
|
1674
1705
|
}) {
|
|
1675
|
-
const labels =
|
|
1706
|
+
const labels = useMemo3(
|
|
1676
1707
|
() => ({ ...defaultSupportChatLabels, ...labelOverrides }),
|
|
1677
1708
|
[labelOverrides]
|
|
1678
1709
|
);
|
|
1679
1710
|
const { config } = useChannlize();
|
|
1680
|
-
const baseUrl = config.baseUrl ||
|
|
1711
|
+
const baseUrl = config.baseUrl || DEFAULT_BASE_URL2;
|
|
1681
1712
|
const [text, setText] = useState5("");
|
|
1682
1713
|
const [files, setFiles] = useState5([]);
|
|
1683
1714
|
const [pending, setPending] = useState5([]);
|
|
@@ -1715,7 +1746,8 @@ function SupportChat({
|
|
|
1715
1746
|
onError,
|
|
1716
1747
|
sessionId,
|
|
1717
1748
|
persistSession,
|
|
1718
|
-
storageKey
|
|
1749
|
+
storageKey,
|
|
1750
|
+
debug
|
|
1719
1751
|
});
|
|
1720
1752
|
const sseFailed = error?.code === "SSE_ERROR";
|
|
1721
1753
|
const ownSendError = !!error && error === sendError;
|
|
@@ -1731,7 +1763,7 @@ function SupportChat({
|
|
|
1731
1763
|
);
|
|
1732
1764
|
const status = sseFailed || startFailed ? "offline" : showConnecting ? "connecting" : "online";
|
|
1733
1765
|
const statusLabel = status === "offline" ? labels.offline : status === "connecting" ? session ? labels.reconnecting : labels.connecting : statusText ? `${labels.online} \xB7 ${statusText}` : labels.online;
|
|
1734
|
-
const displayMessages =
|
|
1766
|
+
const displayMessages = useMemo3(() => {
|
|
1735
1767
|
const fromServer = messages.map(
|
|
1736
1768
|
(m, i) => ({
|
|
1737
1769
|
key: m.id || `m-${i}`,
|
|
@@ -1761,7 +1793,7 @@ function SupportChat({
|
|
|
1761
1793
|
}));
|
|
1762
1794
|
return [...fromServer, ...local];
|
|
1763
1795
|
}, [messages, pending, title, baseUrl]);
|
|
1764
|
-
const sections =
|
|
1796
|
+
const sections = useMemo3(
|
|
1765
1797
|
() => groupMessages(displayMessages),
|
|
1766
1798
|
[displayMessages]
|
|
1767
1799
|
);
|
|
@@ -2303,7 +2335,7 @@ function MessageBubble({
|
|
|
2303
2335
|
onRemove
|
|
2304
2336
|
}) {
|
|
2305
2337
|
const isUser = msg.author === "user";
|
|
2306
|
-
const parts =
|
|
2338
|
+
const parts = useMemo3(() => linkify(msg.content), [msg.content]);
|
|
2307
2339
|
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
2308
2340
|
/* @__PURE__ */ jsxs4(
|
|
2309
2341
|
"div",
|
|
@@ -2600,11 +2632,23 @@ function ChannlizeChat({
|
|
|
2600
2632
|
) }) });
|
|
2601
2633
|
return typeof document !== "undefined" ? createPortal(widget, document.body) : widget;
|
|
2602
2634
|
}
|
|
2635
|
+
|
|
2636
|
+
// src/index.ts
|
|
2637
|
+
import {
|
|
2638
|
+
ChannlizeError,
|
|
2639
|
+
ValidationError,
|
|
2640
|
+
AuthenticationError,
|
|
2641
|
+
RateLimitError
|
|
2642
|
+
} from "@channlize/core";
|
|
2603
2643
|
export {
|
|
2644
|
+
AuthenticationError,
|
|
2604
2645
|
ChannlizeChat,
|
|
2646
|
+
ChannlizeError,
|
|
2605
2647
|
ChannlizeProvider,
|
|
2606
2648
|
ContactForm,
|
|
2649
|
+
RateLimitError,
|
|
2607
2650
|
SupportChat,
|
|
2651
|
+
ValidationError,
|
|
2608
2652
|
defaultContactFormLabels,
|
|
2609
2653
|
defaultSupportChatLabels,
|
|
2610
2654
|
useChannlize,
|