Skip to content

Commit

Permalink
avoid duplicate message fetching and rendering.
Browse files Browse the repository at this point in the history
Prevents duplicate error messages from being fetched on the
initial load.

Does not fetch initial message for the chat pop up if
the popup is hidden

TEST=manual

ran locally, verified this functionality.

Change-Id: Idca1a37622ccc6a362681a8c4f9f7c9994f9fa58
  • Loading branch information
nsiskind committed Oct 16, 2023
1 parent 38947e2 commit d3f5dc5
Show file tree
Hide file tree
Showing 11 changed files with 57 additions and 18 deletions.
13 changes: 13 additions & 0 deletions docs/chat-ui-react.chatpanelprops.ischathidden.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/chat-ui-react](./chat-ui-react.md) &gt; [ChatPanelProps](./chat-ui-react.chatpanelprops.md) &gt; [isChatHidden](./chat-ui-react.chatpanelprops.ischathidden.md)

## ChatPanelProps.isChatHidden property

If chat is hidden, the initial message will not be fetched.

**Signature:**

```typescript
isChatHidden?: boolean;
```
1 change: 1 addition & 0 deletions docs/chat-ui-react.chatpanelprops.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export interface ChatPanelProps extends Omit<MessageBubbleProps, "customCssClass
| --- | --- | --- | --- |
| [customCssClasses?](./chat-ui-react.chatpanelprops.customcssclasses.md) | | [ChatPanelCssClasses](./chat-ui-react.chatpanelcssclasses.md) | _(Optional)_ CSS classes for customizing the component styling. |
| [header?](./chat-ui-react.chatpanelprops.header.md) | | JSX.Element | _(Optional)_ A header to render at the top of the panel. |
| [isChatHidden?](./chat-ui-react.chatpanelprops.ischathidden.md) | | boolean | _(Optional)_ If chat is hidden, the initial message will not be fetched. |
1 change: 1 addition & 0 deletions etc/chat-ui-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface ChatPanelCssClasses {
export interface ChatPanelProps extends Omit<MessageBubbleProps, "customCssClasses" | "message">, Omit<ChatInputProps, "customCssClasses"> {
customCssClasses?: ChatPanelCssClasses;
header?: JSX.Element;
isChatHidden?: boolean;
}

// @public
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yext/chat-ui-react",
"version": "0.7.2",
"version": "0.7.3",
"description": "A library of React Components for powering Yext Chat integrations.",
"author": "[email protected]",
"main": "./lib/commonjs/src/index.js",
Expand Down
2 changes: 1 addition & 1 deletion src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function ChatInput({
const canSendMessage = useChatState(
(state) => state.conversation.canSendMessage
);
const defaultHandleApiError = useDefaultHandleApiError();
const defaultHandleApiError = useDefaultHandleApiError(chat);

const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);

Expand Down
29 changes: 23 additions & 6 deletions src/components/ChatPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export interface ChatPanelProps
* CSS classes for customizing the component styling.
*/
customCssClasses?: ChatPanelCssClasses;
/**
* If chat is hidden, the initial message will not be fetched.
*/
isChatHidden?: boolean;
}

/**
Expand All @@ -65,15 +69,15 @@ export interface ChatPanelProps
* @param props - {@link ChatPanelProps}
*/
export function ChatPanel(props: ChatPanelProps) {
const { header, customCssClasses } = props;
const { header, customCssClasses, isChatHidden } = props;
const chat = useChatActions();
const messages = useChatState((state) => state.conversation.messages);
const loading = useChatState((state) => state.conversation.isLoading);
const canSendMessage = useChatState(
(state) => state.conversation.canSendMessage
);
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
const defaultHandleApiError = useDefaultHandleApiError();
const defaultHandleApiError = useDefaultHandleApiError(chat);
const reportAnalyticsEvent = useReportAnalyticsEvent();

useEffect(() => {
Expand All @@ -84,13 +88,26 @@ export function ChatPanel(props: ChatPanelProps) {

// Fetch the first message on load, if there are no existing messages or a request being processed
useEffect(() => {
if (messages.length !== 0 || !canSendMessage) {
if (messages.length !== 0 || !canSendMessage || isChatHidden) {
return;
}

const { stream = false, handleError } = props;
const res = stream ? chat.streamNextMessage() : chat.getNextMessage();
res.catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));
}, [chat, props, messages, defaultHandleApiError, canSendMessage]);
const messageFetchFunction = stream
? chat.streamNextMessage
: chat.getNextMessage;

messageFetchFunction().catch((e) =>
handleError ? handleError(e) : defaultHandleApiError(e)
);
}, [
chat,
props,
messages,
defaultHandleApiError,
canSendMessage,
isChatHidden,
]);

const messagesRef = useRef<Array<HTMLDivElement | null>>([]);
const messagesContainer = useRef<HTMLDivElement>(null);
Expand Down
1 change: 1 addition & 0 deletions src/components/ChatPopUp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export function ChatPopUp(props: ChatPopUpProps) {
<ChatPanel
{...props}
customCssClasses={cssClasses.panelCssClasses}
isChatHidden={!showChat}
header={
<ChatHeader
title={title}
Expand Down
12 changes: 6 additions & 6 deletions src/hooks/useDefaultHandleApiError.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { MessageSource, useChatActions } from "@yext/chat-headless-react";
import { ChatHeadless, MessageSource } from "@yext/chat-headless-react";
import { useCallback } from "react";

/**
* Returns a default handler function for API errors. It will log the error and
* add a default error message to state.
*
* @internal
*
* @param chatHeadless - {@link ChatHeadless}
*/
export function useDefaultHandleApiError() {
const chat = useChatActions();

export function useDefaultHandleApiError(chatHeadless: ChatHeadless) {
return useCallback(
(e: unknown) => {
console.error(e);
chat.addMessage({
chatHeadless.addMessage({
text: "Sorry, I'm unable to respond at the moment. Please try again later!",
source: MessageSource.BOT,
timestamp: new Date().toISOString(),
});
},
[chat]
[chatHeadless]
);
}
2 changes: 1 addition & 1 deletion test-site/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion tests/components/ChatPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ it("does not render loading dots when loading status is false", () => {
expect(screen.queryByLabelText("Loading Indicator")).not.toBeInTheDocument();
});

it("display message bubbles based on messages in state", () => {
it("displays message bubbles based on messages in state", () => {
mockChatState({
conversation: {
messages: [dummyMessage],
Expand All @@ -122,3 +122,9 @@ it("display message bubbles based on messages in state", () => {
render(<ChatPanel />);
expect(screen.getByText(dummyMessage.text)).toBeInTheDocument();
});

it("does not get initial message when it is hidden", () => {
const actions = spyOnActions();
render(<ChatPanel isChatHidden={true} />);
expect(actions.getNextMessage).toBeCalledTimes(0);
});

0 comments on commit d3f5dc5

Please sign in to comment.