Skip to content

Commit

Permalink
fix: Fix options, checkboxes, block spam bug and add simulate stream …
Browse files Browse the repository at this point in the history
…to user bubble
  • Loading branch information
tjtanjin committed Mar 14, 2024
1 parent d9f1bcc commit 7ee6894
Show file tree
Hide file tree
Showing 13 changed files with 33 additions and 23 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<img height="400px" src="https://github.com/tjtanjin/react-chatbotify/assets/43908963/761fcbb3-858e-4a9c-846b-4fddaf218dbc" />
</p>

React ChatBotify is an intuitive and versatile chatbot library tailored to streamline your development process while providing the flexibility to implement advanced features. It is crafted to meet a wide range of requirements, whether you're building a straightforward FAQ chatbot or an intricate conversational interface.
React ChatBotify is an intuitive and versatile chatbot library tailored to streamline your development process while providing the flexibility to implement advanced features. It is crafted to meet a wide range of requirements, whether you're building a straightforward FAQ chatbot, an intricate conversational interface or even an integration with Large Language Models (LLMs).

React ChatBotify aims to simplify the creation of chatbots by offering a user-friendly experience while accommodating the diverse needs of developers. With its extensive capabilities, you can easily customize and expand your chatbot's functionalities. From basic interactions to sophisticated conversational flows, React ChatBotify empowers you to build chatbots that meet your specific project goals. Head over to our [community showcases](https://github.com/tjtanjin/react-chatbotify/blob/main/SHOWCASES.md) and get inspired to start your own today!

Expand Down
Binary file removed src/assets/demo.png
Binary file not shown.
24 changes: 13 additions & 11 deletions src/components/ChatBotContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -343,25 +343,28 @@ const ChatBotContainer = ({ flow }: { flow: Flow }) => {
* Injects a message at the end of the messages array.
*
* @param content message content to inject
* @param sender sender of the message
* @param sender sender of the message, defaults to bot
*/
const injectMessage = async (content: string | JSX.Element, sender = "bot") => {
const message = {content: content, sender: sender};
processAudio(botOptions, audioToggledOn, message);

const isStream = typeof message.content === "string"
const isBotStream = typeof message.content === "string"
&& message.sender === "bot" && botOptions?.botBubble?.simStream;
const isUserStream = typeof message.content === "string"
&& message.sender === "user" && botOptions?.userBubble?.simStream;

if (isStream) {
await simulateStream(message, botOptions.botBubble?.streamSpeed as number)
if (isBotStream) {
await simulateStream(message, botOptions.botBubble?.streamSpeed as number, "bot");
} else if (isUserStream) {
await simulateStream(message, botOptions.userBubble?.streamSpeed as number, "user");
} else {
setMessages((prevMessages) => [...prevMessages, message]);
}
}

const simulateStream = async (message: Message, streamSpeed: number) => {
const simulateStream = async (message: Message, streamSpeed: number, sender: string) => {
// when simulating stream, disable text area and stop bot typing
setTextAreaDisabled(true);
setIsBotTyping(false);

// set an initial empty message to be used for streaming
Expand All @@ -378,7 +381,7 @@ const ChatBotContainer = ({ flow }: { flow: Flow }) => {
const updatedMessages = [...prevMessages];

for (let i = updatedMessages.length - 1; i >= 0; i--) {
if (updatedMessages[i].sender === "bot" && typeof updatedMessages[i].content === "string") {
if (updatedMessages[i].sender === sender && typeof updatedMessages[i].content === "string") {
message.content = streamMessage.slice(0, streamIndex + 1);
updatedMessages[i] = message;
break;
Expand All @@ -398,7 +401,6 @@ const ChatBotContainer = ({ flow }: { flow: Flow }) => {
// when streaming is done, remove task, unlock text area, and resolve the promise
if (streamIndex === streamMessage.length) {
clearInterval(intervalId);
setTextAreaDisabled(false);
resolve();
}
}, streamSpeed);
Expand All @@ -411,7 +413,7 @@ const ChatBotContainer = ({ flow }: { flow: Flow }) => {
* Streams data into the last message at the end of the messages array with given type.
*
* @param content message content to inject
* @param sender sender of the message
* @param sender sender of the message, defaults to bot
*/
const streamMessage = async (content: string | JSX.Element, sender = "bot") => {
const message = {content: content, sender: sender};
Expand Down Expand Up @@ -503,7 +505,7 @@ const ChatBotContainer = ({ flow }: { flow: Flow }) => {
* @param userInput input provided by the user
* @param sendUserInput boolean indicating if user input should be sent as a message into the chat window
*/
const handleActionInput = (path: string, userInput: string, sendUserInput = true) => {
const handleActionInput = async (path: string, userInput: string, sendUserInput = true) => {
clearTimeout(timeoutId);
userInput = userInput.trim();
paramsInputRef.current = userInput;
Expand All @@ -514,7 +516,7 @@ const ChatBotContainer = ({ flow }: { flow: Flow }) => {

// Add user message to messages array
if (sendUserInput) {
injectMessage(userInput, "user");
await injectMessage(userInput, "user");
}

// Clear input field
Expand Down
2 changes: 1 addition & 1 deletion src/components/ChatBotFooter/ChatBotFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const ChatBotFooter = ({
openChat: (isOpen: boolean) => void;
getCurrPath: () => string | null;
getPrevPath: () => string | null;
handleActionInput: (path: string, userInput: string, sendUserInput?: boolean) => void;
handleActionInput: (path: string, userInput: string, sendUserInput?: boolean) => Promise<void>;
}) => {

// handles options for bot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const FileAttachmentButton = ({
openChat: (isOpen: boolean) => void;
getCurrPath: () => string | null;
getPrevPath: () => string | null;
handleActionInput: (path: string, userInput: string, sendUserInput?: boolean) => void;
handleActionInput: (path: string, userInput: string, sendUserInput?: boolean) => Promise<void>;
}) => {

// handles options for bot
Expand Down Expand Up @@ -84,7 +84,7 @@ const FileAttachmentButton = ({
for (let i = 0; i < files.length; i++) {
fileNames.push(files[i].name);
}
handleActionInput(currPath, "📄 " + fileNames.join(", "), botOptions.chatInput?.sendAttachmentOutput);
await handleActionInput(currPath, "📄 " + fileNames.join(", "), botOptions.chatInput?.sendAttachmentOutput);
await fileHandler({userInput: inputRef.current?.value as string, prevPath: getPrevPath(),
injectMessage, streamMessage, openChat, files});
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ChatBotInput/ChatBotInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const ChatBotInput = ({
voiceToggledOn: boolean;
getCurrPath: () => string | null;
handleToggleVoice: () => void;
handleActionInput: (path: string, userInput: string, sendUserInput?: boolean) => void;
handleActionInput: (path: string, userInput: string, sendUserInput?: boolean) => Promise<void>;
}) => {

// handles options for bot
Expand Down
6 changes: 4 additions & 2 deletions src/components/UserCheckboxes/UserCheckboxes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const UserCheckboxes = ({
checkboxes: {items: Array<string>, max?: number, min?: number};
checkedItems: Set<string>;
path: string;
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => void;
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => Promise<void>;
}) => {

// handles options for bot
Expand Down Expand Up @@ -71,7 +71,8 @@ const UserCheckboxes = ({
...botOptions.botCheckMarkSelectedStyle
};

// disables checkboxes when moving on from current path
// when moving on from current path, we also want to disable checkboxes
// cannot just rely on user input since path can change even without it (e.g. transition)
useEffect(() => {
if (paths.length > 0 && paths[paths.length - 1] !== path) {
setDisabled(true);
Expand Down Expand Up @@ -131,6 +132,7 @@ const UserCheckboxes = ({
onMouseDown={(event: MouseEvent) => {
event.preventDefault();
const userInput = Array.from(checkedItems).join(", ");
setDisabled(true);
handleActionInput(path, userInput, botOptions.chatInput?.sendCheckboxOutput as boolean);
}}
>
Expand Down
6 changes: 4 additions & 2 deletions src/components/UserOptions/UserOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const UserOptions= ({
}: {
options: string[];
path: string;
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => void;
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => Promise<void>;
}) => {

// handles options for bot
Expand Down Expand Up @@ -52,7 +52,8 @@ const UserOptions= ({
...botOptions.botOptionHoveredStyle
};

// disables options when moving on from current path
// when moving on from current path, we also want to disable options
// cannot just rely on user input since path can change even without it (e.g. transition)
useEffect(() => {
if (paths.length > 0 && paths[paths.length - 1] !== path) {
setDisabled(true);
Expand Down Expand Up @@ -99,6 +100,7 @@ const UserOptions= ({
return;
}

setDisabled(true);
handleActionInput(path, key, botOptions.chatInput?.sendOptionOutput as boolean);
}}
>
Expand Down
2 changes: 1 addition & 1 deletion src/services/BlockService/BlockService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { Params } from "../../types/Params";
export const preProcessBlock = async (flow: Flow, path: string, params: Params,
setTextAreaDisabled: (inputDisabled: boolean) => void, setPaths: Dispatch<SetStateAction<string[]>>,
setTimeoutId: (timeoutId: ReturnType<typeof setTimeout>) => void,
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => void) => {
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => Promise<void>) => {

const block = flow[path];
const attributes = Object.keys(block);
Expand Down
2 changes: 1 addition & 1 deletion src/services/BlockService/CheckboxProcessor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Block } from "../../types/Block";
*/
export const processCheckboxes = (block: Block, path: string,
injectMessage: (content: string | JSX.Element, sender?: string) => void,
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => void) => {
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => Promise<void>) => {

const checkboxes = block.checkboxes;
if (checkboxes == null) {
Expand Down
2 changes: 1 addition & 1 deletion src/services/BlockService/OptionProcessor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Block } from "../../types/Block";
*/
export const processOptions = (block: Block, path: string,
injectMessage: (content: string | JSX.Element, sender?: string) => void,
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => void) => {
handleActionInput: (path: string, userInput: string, sendUserInput: boolean) => Promise<void>) => {

const options = block.options;
if (options == null) {
Expand Down
2 changes: 2 additions & 0 deletions src/services/BotOptionsService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ const defaultOptions = {
userBubble: {
showAvatar: false,
avatar: userAvatar,
simStream: false,
streamSpeed: 30,
},
botBubble: {
showAvatar: false,
Expand Down
2 changes: 2 additions & 0 deletions src/types/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export type Options = {
userBubble?: {
showAvatar?: boolean;
avatar?: string;
simStream?: boolean,
streamSpeed? :number,
},
botBubble?: {
showAvatar?: boolean;
Expand Down

0 comments on commit 7ee6894

Please sign in to comment.