@yext/chat-ui-react 0.8.8 → 0.9.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/THIRD-PARTY-NOTICES +52 -81
- package/lib/bundle.css +1 -1
- package/lib/commonjs/package.json.js +1 -1
- package/lib/commonjs/src/components/ChatInput.d.ts +6 -1
- package/lib/commonjs/src/components/ChatInput.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatInput.js +8 -10
- package/lib/commonjs/src/components/ChatInput.js.map +1 -1
- package/lib/commonjs/src/components/ChatPanel.d.ts +5 -0
- package/lib/commonjs/src/components/ChatPanel.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatPanel.js +15 -4
- package/lib/commonjs/src/components/ChatPanel.js.map +1 -1
- package/lib/commonjs/src/components/MessageSuggestions.d.ts +6 -0
- package/lib/commonjs/src/components/MessageSuggestions.d.ts.map +1 -1
- package/lib/commonjs/src/components/MessageSuggestions.js +9 -4
- package/lib/commonjs/src/components/MessageSuggestions.js.map +1 -1
- package/lib/commonjs/src/hooks/useSendMessageWithRetries.d.ts +17 -0
- package/lib/commonjs/src/hooks/useSendMessageWithRetries.d.ts.map +1 -0
- package/lib/commonjs/src/hooks/useSendMessageWithRetries.js +52 -0
- package/lib/commonjs/src/hooks/useSendMessageWithRetries.js.map +1 -0
- package/lib/esm/index.d.ts +17 -1
- package/lib/esm/package.json.mjs +1 -1
- package/lib/esm/src/components/ChatInput.d.ts +6 -1
- package/lib/esm/src/components/ChatInput.d.ts.map +1 -1
- package/lib/esm/src/components/ChatInput.mjs +9 -11
- package/lib/esm/src/components/ChatInput.mjs.map +1 -1
- package/lib/esm/src/components/ChatPanel.d.ts +5 -0
- package/lib/esm/src/components/ChatPanel.d.ts.map +1 -1
- package/lib/esm/src/components/ChatPanel.mjs +16 -5
- package/lib/esm/src/components/ChatPanel.mjs.map +1 -1
- package/lib/esm/src/components/MessageSuggestions.d.ts +6 -0
- package/lib/esm/src/components/MessageSuggestions.d.ts.map +1 -1
- package/lib/esm/src/components/MessageSuggestions.mjs +9 -4
- package/lib/esm/src/components/MessageSuggestions.mjs.map +1 -1
- package/lib/esm/src/hooks/useSendMessageWithRetries.d.ts +17 -0
- package/lib/esm/src/hooks/useSendMessageWithRetries.d.ts.map +1 -0
- package/lib/esm/src/hooks/useSendMessageWithRetries.mjs +50 -0
- package/lib/esm/src/hooks/useSendMessageWithRetries.mjs.map +1 -0
- package/package.json +2 -2
- package/src/components/ChatInput.tsx +14 -11
- package/src/components/ChatPanel.tsx +33 -2
- package/src/components/MessageSuggestions.tsx +17 -3
- package/src/hooks/useSendMessageWithRetries.ts +49 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React, { useCallback, useState } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useChatState } from "@yext/chat-headless-react";
|
|
3
3
|
import { ArrowIcon } from "../icons/Arrow";
|
|
4
4
|
import { useComposedCssClasses } from "../hooks";
|
|
5
5
|
import TextareaAutosize from "react-textarea-autosize";
|
|
6
6
|
import { useDefaultHandleApiError } from "../hooks/useDefaultHandleApiError";
|
|
7
7
|
import { withStylelessCssClasses } from "../utils/withStylelessCssClasses";
|
|
8
|
+
import { useSendMessageWithRetries } from "../hooks/useSendMessageWithRetries";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* The CSS class interface for the {@link ChatInput} component.
|
|
@@ -58,6 +59,11 @@ export interface ChatInputProps {
|
|
|
58
59
|
customCssClasses?: ChatInputCssClasses;
|
|
59
60
|
/** A callback which is called when user sends a message. */
|
|
60
61
|
onSend?: (message: string) => void;
|
|
62
|
+
/**
|
|
63
|
+
* A function which is called when a retryable error occurs from
|
|
64
|
+
* Chat API while processing the user's message.
|
|
65
|
+
*/
|
|
66
|
+
onRetry?: (e: unknown) => void
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
/**
|
|
@@ -79,27 +85,24 @@ export function ChatInput({
|
|
|
79
85
|
sendButtonIcon = <ArrowIcon />,
|
|
80
86
|
customCssClasses,
|
|
81
87
|
onSend,
|
|
88
|
+
onRetry,
|
|
82
89
|
}: ChatInputProps) {
|
|
83
|
-
const chat = useChatActions();
|
|
84
90
|
const [input, setInput] = useState("");
|
|
85
91
|
const canSendMessage = useChatState(
|
|
86
92
|
(state) => state.conversation.canSendMessage
|
|
87
93
|
);
|
|
88
94
|
const defaultHandleApiError = useDefaultHandleApiError();
|
|
89
|
-
|
|
95
|
+
const sendMessageWithRetries = useSendMessageWithRetries(stream, 1, onRetry)
|
|
90
96
|
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
91
97
|
|
|
92
98
|
const sendMessage = useCallback(async () => {
|
|
93
|
-
const res = stream
|
|
94
|
-
? chat.streamNextMessage(input)
|
|
95
|
-
: chat.getNextMessage(input);
|
|
96
99
|
setInput("");
|
|
97
|
-
|
|
98
|
-
.
|
|
99
|
-
|
|
100
|
+
sendMessageWithRetries(input)
|
|
101
|
+
.catch(handleError ?? defaultHandleApiError)
|
|
102
|
+
.finally(() => {
|
|
103
|
+
onSend?.(input)
|
|
100
104
|
})
|
|
101
|
-
|
|
102
|
-
}, [chat, input, handleError, defaultHandleApiError, stream, onSend]);
|
|
105
|
+
}, [sendMessageWithRetries, input, handleError, defaultHandleApiError, onSend]);
|
|
103
106
|
|
|
104
107
|
const handleKeyDown = useCallback(
|
|
105
108
|
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
@@ -4,6 +4,7 @@ import React, {
|
|
|
4
4
|
useEffect,
|
|
5
5
|
useMemo,
|
|
6
6
|
useRef,
|
|
7
|
+
useState,
|
|
7
8
|
} from "react";
|
|
8
9
|
import { useChatState } from "@yext/chat-headless-react";
|
|
9
10
|
import {
|
|
@@ -76,6 +77,11 @@ export interface ChatPanelProps
|
|
|
76
77
|
messageSuggestions?: string[];
|
|
77
78
|
/** A callback which is called when user clicks a link. */
|
|
78
79
|
onLinkClick?: (href?: string) => void;
|
|
80
|
+
/**
|
|
81
|
+
* Text to display when retrying.
|
|
82
|
+
* Defaults to "Error occurred. Retrying".
|
|
83
|
+
*/
|
|
84
|
+
retryText?: string;
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
/**
|
|
@@ -96,6 +102,9 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
96
102
|
handleError,
|
|
97
103
|
messageSuggestions,
|
|
98
104
|
onLinkClick,
|
|
105
|
+
onSend:onSendProp,
|
|
106
|
+
onRetry:onRetryProp,
|
|
107
|
+
retryText = "Error occurred. Retrying",
|
|
99
108
|
} = props;
|
|
100
109
|
const messages = useChatState((state) => state.conversation.messages);
|
|
101
110
|
const loading = useChatState((state) => state.conversation.isLoading);
|
|
@@ -106,6 +115,17 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
106
115
|
const reportAnalyticsEvent = useReportAnalyticsEvent();
|
|
107
116
|
useFetchInitialMessage(handleError, stream);
|
|
108
117
|
|
|
118
|
+
const [retry, setRetry] = useState(false);
|
|
119
|
+
const onSend = useCallback((message: string) => {
|
|
120
|
+
onSendProp?.(message);
|
|
121
|
+
setRetry(false)
|
|
122
|
+
}, [onSendProp])
|
|
123
|
+
|
|
124
|
+
const onRetry = useCallback((e: unknown) => {
|
|
125
|
+
onRetryProp?.(e);
|
|
126
|
+
setRetry(true)
|
|
127
|
+
}, [onRetryProp])
|
|
128
|
+
|
|
109
129
|
useEffect(() => {
|
|
110
130
|
reportAnalyticsEvent({
|
|
111
131
|
action: "CHAT_IMPRESSION",
|
|
@@ -174,18 +194,29 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
174
194
|
/>
|
|
175
195
|
</div>
|
|
176
196
|
))}
|
|
177
|
-
{loading && <
|
|
197
|
+
{loading && <div className="flex">
|
|
198
|
+
<LoadingDots />
|
|
199
|
+
{retry && <p className="text-slate-500 text-[13px] font-bold">{retryText}</p>}
|
|
200
|
+
</div>}
|
|
178
201
|
</div>
|
|
179
202
|
</div>
|
|
180
203
|
<div className={cssClasses.inputContainer}>
|
|
181
204
|
{suggestions && (
|
|
182
205
|
<MessageSuggestions
|
|
206
|
+
stream={stream}
|
|
207
|
+
onSend={onSend}
|
|
208
|
+
onRetry={onRetry}
|
|
183
209
|
handleError={handleError}
|
|
184
210
|
suggestions={suggestions}
|
|
185
211
|
customCssClasses={cssClasses.messageSuggestionClasses}
|
|
186
212
|
/>
|
|
187
213
|
)}
|
|
188
|
-
<ChatInput
|
|
214
|
+
<ChatInput
|
|
215
|
+
{...props}
|
|
216
|
+
onSend={onSend}
|
|
217
|
+
onRetry={onRetry}
|
|
218
|
+
customCssClasses={cssClasses.inputCssClasses}
|
|
219
|
+
/>
|
|
189
220
|
</div>
|
|
190
221
|
{footer && (
|
|
191
222
|
<Markdown
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import { useDefaultHandleApiError } from "../hooks/useDefaultHandleApiError";
|
|
8
8
|
import { withStylelessCssClasses } from "../utils/withStylelessCssClasses";
|
|
9
9
|
import { useComposedCssClasses } from "../hooks";
|
|
10
|
+
import { useSendMessageWithRetries } from "../hooks/useSendMessageWithRetries";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* The CSS class interface for the MessageSuggestion component.
|
|
@@ -30,6 +31,12 @@ export interface MessageSuggestionsProps {
|
|
|
30
31
|
handleError?: (e: unknown) => void;
|
|
31
32
|
/** CSS classes for customizing the component styling. */
|
|
32
33
|
customCssClasses?: MessageSuggestionCssClasses;
|
|
34
|
+
/** {@inheritdoc ChatInputProps.stream} */
|
|
35
|
+
stream?: boolean;
|
|
36
|
+
/** {@inheritdoc ChatInputProps.onSend} */
|
|
37
|
+
onSend?: (message: string) => void;
|
|
38
|
+
/** {@inheritdoc ChatInputProps.onRetry} */
|
|
39
|
+
onRetry?: (e: unknown) => void
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
const defaultClassnames: MessageSuggestionCssClasses = withStylelessCssClasses(
|
|
@@ -51,10 +58,14 @@ export const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({
|
|
|
51
58
|
handleError,
|
|
52
59
|
suggestions,
|
|
53
60
|
customCssClasses,
|
|
61
|
+
stream = false,
|
|
62
|
+
onSend,
|
|
63
|
+
onRetry,
|
|
54
64
|
}) => {
|
|
55
65
|
const actions = useChatActions();
|
|
56
66
|
const notes = useChatState((state) => state.conversation.notes);
|
|
57
67
|
const defaultHandleApiError = useDefaultHandleApiError();
|
|
68
|
+
const sendMessageWithRetries = useSendMessageWithRetries(stream, 1, onRetry)
|
|
58
69
|
const sendMsg = useCallback(
|
|
59
70
|
(msg: string) => {
|
|
60
71
|
const newNotes = {
|
|
@@ -62,10 +73,13 @@ export const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({
|
|
|
62
73
|
suggestedReplies: undefined,
|
|
63
74
|
} satisfies MessageNotes;
|
|
64
75
|
actions.setMessageNotes(newNotes);
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
sendMessageWithRetries(msg)
|
|
77
|
+
.catch(handleError ?? defaultHandleApiError)
|
|
78
|
+
.finally(() => {
|
|
79
|
+
onSend?.(msg)
|
|
80
|
+
})
|
|
67
81
|
},
|
|
68
|
-
[actions,
|
|
82
|
+
[notes, actions, sendMessageWithRetries, handleError, defaultHandleApiError, onSend]
|
|
69
83
|
);
|
|
70
84
|
|
|
71
85
|
const classes = useComposedCssClasses(defaultClassnames, customCssClasses);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ApiError, useChatActions } from "@yext/chat-headless-react";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns a function that sends a message to the chat API with retries
|
|
6
|
+
* if the API returns a 5xx status code.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* The function will throw the error if the maximum number of retries is reached
|
|
10
|
+
* or if the error is not a 5xx status code.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*
|
|
14
|
+
* @param stream - If true, use streaming API
|
|
15
|
+
* @param maxRetries - Maximum number of retries
|
|
16
|
+
* @param onRetry - Callback to handle error on each retry
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
export function useSendMessageWithRetries(
|
|
20
|
+
stream = false,
|
|
21
|
+
maxRetries = 0,
|
|
22
|
+
onRetry?: (e: unknown) => void
|
|
23
|
+
): (input: string) => Promise<void> {
|
|
24
|
+
const chat = useChatActions()
|
|
25
|
+
return useCallback(async (input: string) => {
|
|
26
|
+
let err: unknown;
|
|
27
|
+
let text = input;
|
|
28
|
+
for (let numRetries = 0; numRetries <= maxRetries; numRetries++) {
|
|
29
|
+
if (numRetries > 0 && !!err) {
|
|
30
|
+
if (err instanceof ApiError && !!err.statusCode && err.statusCode >= 500) {
|
|
31
|
+
onRetry?.(err)
|
|
32
|
+
// avoid re-adding user message to conversation history on retry
|
|
33
|
+
text = "";
|
|
34
|
+
} else {
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await (stream
|
|
40
|
+
? chat.streamNextMessage(text)
|
|
41
|
+
: chat.getNextMessage(text));
|
|
42
|
+
return;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
err = e;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
throw err
|
|
48
|
+
}, [chat, maxRetries, onRetry, stream])
|
|
49
|
+
}
|