);
}
diff --git a/package-lock.json b/package-lock.json
index 58478ee..66145fb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18722,7 +18722,7 @@
},
"packages/chat-headless": {
"name": "@yext/chat-headless",
- "version": "0.10.0",
+ "version": "0.10.1",
"license": "BSD-3-Clause",
"dependencies": {
"@reduxjs/toolkit": "^1.9.5",
@@ -19047,6 +19047,16 @@
"@types/yargs-parser": "*"
}
},
+ "packages/chat-headless-react/node_modules/@yext/chat-headless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@yext/chat-headless/-/chat-headless-0.10.0.tgz",
+ "integrity": "sha512-rYabLNfU8KcMDw/HqAkWbFuu2AavSjEtU5kYYAVdTRcS3LyrsNXe/l7lp11R3RCmJCcJnn5TXqhcTKzfI8I9zA==",
+ "dependencies": {
+ "@reduxjs/toolkit": "^1.9.5",
+ "@yext/analytics": "^0.6.3",
+ "@yext/chat-core": "^0.8.2"
+ }
+ },
"packages/chat-headless-react/node_modules/ansi-styles": {
"version": "4.3.0",
"dev": true,
diff --git a/packages/chat-headless-react/THIRD-PARTY-NOTICES b/packages/chat-headless-react/THIRD-PARTY-NOTICES
index 3c61571..02de88c 100644
--- a/packages/chat-headless-react/THIRD-PARTY-NOTICES
+++ b/packages/chat-headless-react/THIRD-PARTY-NOTICES
@@ -3,7 +3,7 @@ https://www.npmjs.com/package/generate-license-file
The following npm package may be included in this product:
- - @babel/runtime@7.24.7
+ - @babel/runtime@7.24.8
This package contains the following license and notice below:
diff --git a/packages/chat-headless/THIRD-PARTY-NOTICES b/packages/chat-headless/THIRD-PARTY-NOTICES
index 71fd972..bf44b66 100644
--- a/packages/chat-headless/THIRD-PARTY-NOTICES
+++ b/packages/chat-headless/THIRD-PARTY-NOTICES
@@ -3,7 +3,7 @@ https://www.npmjs.com/package/generate-license-file
The following npm package may be included in this product:
- - @babel/runtime@7.24.7
+ - @babel/runtime@7.24.8
This package contains the following license and notice below:
diff --git a/packages/chat-headless/docs/chat-headless.chateventclient.md b/packages/chat-headless/docs/chat-headless.chateventclient.md
index f5b061c..f6457d1 100644
--- a/packages/chat-headless/docs/chat-headless.chateventclient.md
+++ b/packages/chat-headless/docs/chat-headless.chateventclient.md
@@ -21,4 +21,5 @@ export interface ChatEventClient
| [init(messageResponse)](./chat-headless.chateventclient.init.md) | Initializes the client, using credentials and data in the provided message to setup a chat session. |
| [on(eventName, cb)](./chat-headless.chateventclient.on.md) | Registers an event listener for a specified event. Supported events are: - message: A new message has been received. - typing: The agent is typing. - close: The chat session has been closed. |
| [processMessage(request)](./chat-headless.chateventclient.processmessage.md) | Processes a message request. The response should be emitted as a message event. |
+| [resetSession()](./chat-headless.chateventclient.resetsession.md) | Reset the current chat session. |
diff --git a/packages/chat-headless/docs/chat-headless.chateventclient.resetsession.md b/packages/chat-headless/docs/chat-headless.chateventclient.resetsession.md
new file mode 100644
index 0000000..cd783a3
--- /dev/null
+++ b/packages/chat-headless/docs/chat-headless.chateventclient.resetsession.md
@@ -0,0 +1,17 @@
+
+
+[Home](./index.md) > [@yext/chat-headless](./chat-headless.md) > [ChatEventClient](./chat-headless.chateventclient.md) > [resetSession](./chat-headless.chateventclient.resetsession.md)
+
+## ChatEventClient.resetSession() method
+
+Reset the current chat session.
+
+**Signature:**
+
+```typescript
+resetSession(): void;
+```
+**Returns:**
+
+void
+
diff --git a/packages/chat-headless/docs/chat-headless.chatheadless.md b/packages/chat-headless/docs/chat-headless.chatheadless.md
index 75f8748..94db4c2 100644
--- a/packages/chat-headless/docs/chat-headless.chatheadless.md
+++ b/packages/chat-headless/docs/chat-headless.chatheadless.md
@@ -21,7 +21,7 @@ export interface ChatHeadless
| [getNextMessage(text, source)](./chat-headless.chatheadless.getnextmessage.md) | Performs a Chat API request for the next message generated by chat bot using the conversation state (e.g. message history and notes). Update the state with the response data. |
| [initLocalStorage()](./chat-headless.chatheadless.initlocalstorage.md) | Loads the [ConversationState](./chat-headless.conversationstate.md) from local storage, if present, and adds a listener to keep the conversation state in sync with the stored state |
| [report(eventPayload)](./chat-headless.chatheadless.report.md) | Send Chat related analytics event to Yext Analytics API. |
-| [restartConversation()](./chat-headless.chatheadless.restartconversation.md) | Resets all fields within [ConversationState](./chat-headless.conversationstate.md) |
+| [restartConversation()](./chat-headless.chatheadless.restartconversation.md) |
Resets all fields within the [ConversationState](./chat-headless.conversationstate.md), and sets the active client to the bot client, if one was provided when constructing the [ChatHeadless](./chat-headless.chatheadless.md) instance.
If a [ChatEventClient](./chat-headless.chateventclient.md) is currently active before reset, that client's resetSession method is called.
|
| [setCanSendMessage(canSendMessage)](./chat-headless.chatheadless.setcansendmessage.md) | Sets [ConversationState.canSendMessage](./chat-headless.conversationstate.cansendmessage.md) to the specified state |
| [setChatLoadingStatus(isLoading)](./chat-headless.chatheadless.setchatloadingstatus.md) | Sets [ConversationState.isLoading](./chat-headless.conversationstate.isloading.md) to the specified loading state |
| [setContext(context)](./chat-headless.chatheadless.setcontext.md) | Sets [MetaState.context](./chat-headless.metastate.context.md) to the specified context. |
diff --git a/packages/chat-headless/docs/chat-headless.chatheadless.restartconversation.md b/packages/chat-headless/docs/chat-headless.chatheadless.restartconversation.md
index 0caa99b..42edde7 100644
--- a/packages/chat-headless/docs/chat-headless.chatheadless.restartconversation.md
+++ b/packages/chat-headless/docs/chat-headless.chatheadless.restartconversation.md
@@ -4,7 +4,9 @@
## ChatHeadless.restartConversation() method
-Resets all fields within [ConversationState](./chat-headless.conversationstate.md)
+Resets all fields within the [ConversationState](./chat-headless.conversationstate.md), and sets the active client to the `bot` client, if one was provided when constructing the [ChatHeadless](./chat-headless.chatheadless.md) instance.
+
+If a [ChatEventClient](./chat-headless.chateventclient.md) is currently active before reset, that client's `resetSession` method is called.
**Signature:**
diff --git a/packages/chat-headless/etc/chat-headless.api.md b/packages/chat-headless/etc/chat-headless.api.md
index d1b5195..bc24c62 100644
--- a/packages/chat-headless/etc/chat-headless.api.md
+++ b/packages/chat-headless/etc/chat-headless.api.md
@@ -44,6 +44,7 @@ export interface ChatEventClient {
init(messageResponse: MessageResponse): Promise;
on(eventName: "message" | "typing" | "close", cb: (data: any) => void): void;
processMessage(request: MessageRequest): Promise;
+ resetSession(): void;
}
// @public
diff --git a/packages/chat-headless/package.json b/packages/chat-headless/package.json
index 170d381..4453e12 100644
--- a/packages/chat-headless/package.json
+++ b/packages/chat-headless/package.json
@@ -1,6 +1,6 @@
{
"name": "@yext/chat-headless",
- "version": "0.10.0",
+ "version": "0.10.1",
"description": "A state manager library powered by Redux for Yext Chat integrations",
"main": "./dist/commonjs/src/index.js",
"module": "./dist/esm/src/index.mjs",
diff --git a/packages/chat-headless/src/ChatHeadlessImpl.ts b/packages/chat-headless/src/ChatHeadlessImpl.ts
index 4d9c94c..e5aaa5a 100644
--- a/packages/chat-headless/src/ChatHeadlessImpl.ts
+++ b/packages/chat-headless/src/ChatHeadlessImpl.ts
@@ -44,6 +44,7 @@ import { isChatEventClient } from "./models/clients/ChatEventClient";
export class ChatHeadlessImpl implements ChatHeadless {
private config: HeadlessConfig;
private chatClient: ChatClient;
+ private botClient: ChatClient;
private clients: ChatClient[];
private stateManager: ReduxStateManager;
private chatAnalyticsService: ChatAnalyticsService;
@@ -71,6 +72,7 @@ export class ChatHeadlessImpl implements ChatHeadless {
// bot client is the default client.
// If agent client is provided, it will be used as the second client on handoff
this.chatClient = botClient ?? provideChatCore(this.config);
+ this.botClient = this.chatClient;
this.clients = [this.chatClient];
if (agentClient) {
this.clients.push(agentClient);
@@ -264,6 +266,11 @@ export class ChatHeadlessImpl implements ChatHeadless {
}
restartConversation() {
+ if (isChatEventClient(this.chatClient)) {
+ this.chatClient.resetSession();
+ }
+ this.chatClient = this.botClient;
+
this.setConversationId(undefined);
this.setChatLoadingStatus(false);
this.setCanSendMessage(true);
diff --git a/packages/chat-headless/src/models/ChatHeadless.ts b/packages/chat-headless/src/models/ChatHeadless.ts
index 443c691..7d1ce6f 100644
--- a/packages/chat-headless/src/models/ChatHeadless.ts
+++ b/packages/chat-headless/src/models/ChatHeadless.ts
@@ -110,7 +110,12 @@ export interface ChatHeadless {
*/
initLocalStorage(): void;
/**
- * Resets all fields within {@link ConversationState}
+ * Resets all fields within the {@link ConversationState}, and sets the active
+ * client to the `bot` client, if one was provided when constructing the
+ * {@link ChatHeadless} instance.
+ *
+ * If a {@link ChatEventClient} is currently active before reset, that client's
+ * `resetSession` method is called.
*
* @public
*/
diff --git a/packages/chat-headless/src/models/clients/ChatEventClient.ts b/packages/chat-headless/src/models/clients/ChatEventClient.ts
index b1f74f1..b06ad3b 100644
--- a/packages/chat-headless/src/models/clients/ChatEventClient.ts
+++ b/packages/chat-headless/src/models/clients/ChatEventClient.ts
@@ -48,6 +48,11 @@ export interface ChatEventClient {
* Provide the current chat session.
*/
getSession(): any;
+
+ /**
+ * Reset the current chat session.
+ */
+ resetSession(): void;
}
export function isChatEventClient(
diff --git a/packages/chat-headless/tests/chatheadless.clients.test.ts b/packages/chat-headless/tests/chatheadless.clients.test.ts
index 8eed381..6d8ee43 100644
--- a/packages/chat-headless/tests/chatheadless.clients.test.ts
+++ b/packages/chat-headless/tests/chatheadless.clients.test.ts
@@ -125,6 +125,37 @@ it("update state on events from event client", async () => {
expect(headless.state.conversation.isLoading).toBeFalsy();
});
+it("resets session and uses bot client on reset", async () => {
+ const botClient = createMockHttpClient([
+ { message: createMessage("message 1"), notes: {}, integrationDetails: {} }, //trigger handoff
+ { message: createMessage("message 2"), notes: {} },
+ ]);
+ const callbacks: Record = {};
+ const agentClient = createMockEventClient(callbacks);
+ const headless = provideChatHeadless(config, {
+ bot: botClient,
+ agent: agentClient,
+ });
+
+ // start with bot client, immediately trigger handoff
+ await headless.getNextMessage();
+ expect(botClient.getNextMessage).toHaveBeenCalledTimes(1);
+ expect(agentClient.init).toHaveBeenCalledTimes(1);
+
+ // with agent client, get next message
+ await headless.getNextMessage();
+ expect(agentClient.processMessage).toHaveBeenCalledTimes(1);
+
+ // reset session, switching back to bot client
+ headless.restartConversation();
+ expect(agentClient.resetSession).toHaveBeenCalledTimes(1);
+ expect(agentClient.getSession()).toBeUndefined();
+
+ // with bot client, get next message
+ await headless.getNextMessage();
+ expect(botClient.getNextMessage).toHaveBeenCalledTimes(2);
+});
+
function createMessage(text: string): Message {
return {
text,
@@ -149,21 +180,28 @@ function createMockHttpClient(
}
function createMockEventClient(
- callbacks: Record
+ callbacks?: Record
): ChatEventClient {
const client: ChatEventClient = {
init: jest.fn(),
on: (event, cb) => {
+ if (!callbacks) {
+ return;
+ }
if (!callbacks[event]) {
callbacks[event] = [];
}
callbacks[event].push(cb);
},
processMessage: jest.fn(async () => {
+ if (!callbacks) {
+ return;
+ }
callbacks["message"]?.forEach((cb) => cb("bot message"));
}),
emit: jest.fn(),
getSession: jest.fn(),
+ resetSession: jest.fn(),
};
return client;
}
diff --git a/packages/chat-headless/tests/chatheadless.test.ts b/packages/chat-headless/tests/chatheadless.test.ts
index bdc26aa..0eae889 100644
--- a/packages/chat-headless/tests/chatheadless.test.ts
+++ b/packages/chat-headless/tests/chatheadless.test.ts
@@ -1,4 +1,5 @@
import {
+ ChatHttpClient,
ConversationState,
HeadlessConfig,
Message,
@@ -28,7 +29,13 @@ const mockedMetaState: MetaState = {
};
beforeEach(() => {
- jest.spyOn(coreLib, "provideChatCore").mockImplementation();
+ jest.spyOn(coreLib, "provideChatCore").mockImplementation(() => {
+ const client: ChatHttpClient = {
+ getNextMessage: jest.fn(),
+ streamNextMessage: jest.fn(),
+ };
+ return client;
+ });
localStorage.clear();
});