feat(frontend): typing chat feature | for #187 (#353)

* feat(frontend): typing chat

* fix linting

* typo corrections - ChatInterface.tsx

typo corrections in comments

* refactor and fix typing
This commit is contained in:
808vita 2024-04-01 00:40:37 +05:30 committed by GitHub
parent c6cc5f6b5a
commit a00f604995
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 94 additions and 8 deletions

View File

@ -6,6 +6,31 @@ import userAvatar from "../assets/user-avatar.png";
import { sendChatMessage } from "../services/chatService";
import { RootState } from "../store";
import CogTooth from "../assets/cog-tooth";
import { useTypingEffect } from "../hooks/useTypingEffect";
import { Message } from "../state/chatSlice";
interface ITypingChatProps {
msg: Message;
}
/**
* @param msg
* @returns jsx
*
* component used for typing effect when assistant replies
*
* makes uses of useTypingEffect hook
*
*/
function TypingChat({ msg }: ITypingChatProps) {
return (
msg?.content && (
<Card>
<CardBody>{useTypingEffect([msg?.content], { loop: false })}</CardBody>
</Card>
)
);
}
function MessageList(): JSX.Element {
const messagesEndRef = useRef<HTMLDivElement>(null);
@ -18,20 +43,22 @@ function MessageList(): JSX.Element {
return (
<div className="flex-1 overflow-y-auto">
{messages.map((msg, index) => (
<div key={index} className="flex mb-2.5">
<div key={index} className="flex mb-2.5 pr-5 pl-5">
<div
className={`${msg.sender === "user" ? "flex flex-row-reverse mt-2.5 mr-2.5 mb-0 ml-auto" : "flex"}`}
className={`flex mt-2.5 mb-0 ${msg.sender === "user" && "flex-row-reverse ml-auto"}`}
>
<img
src={msg.sender === "user" ? userAvatar : assistantAvatar}
alt={`${msg.sender} avatar`}
className="w-[40px] h-[40px] mx-2.5"
/>
<Card
className={`w-4/5 ${msg.sender === "user" ? "bg-primary" : ""}`}
>
<CardBody>{msg.content}</CardBody>
</Card>
{msg.sender !== "user" ? (
<TypingChat msg={msg} />
) : (
<Card className="bg-primary">
<CardBody>{msg.content}</CardBody>
</Card>
)}
</div>
</div>
))}

View File

@ -0,0 +1,59 @@
import { useEffect, useState } from "react";
/**
* hook to be used for typing chat effect
*/
export const useTypingEffect = (
strings: string[] = [""],
{
loop = false,
playbackRate = 0.1,
}: { loop?: boolean; playbackRate?: number } = {
loop: false,
playbackRate: 0.1,
},
) => {
// eslint-disable-next-line prefer-const
let [{ stringIndex, characterIndex }, setState] = useState<{
stringIndex: number;
characterIndex: number;
}>({
stringIndex: 0,
characterIndex: 0,
});
let timeoutId: number;
const emulateKeyStroke = () => {
// eslint-disable-next-line no-plusplus
characterIndex++;
if (characterIndex === strings[stringIndex].length) {
characterIndex = 0;
// eslint-disable-next-line no-plusplus
stringIndex++;
if (stringIndex === strings.length) {
if (!loop) {
return;
}
stringIndex = 0;
}
timeoutId = window.setTimeout(emulateKeyStroke, 100 * playbackRate);
} else if (characterIndex === strings[stringIndex].length - 1) {
timeoutId = window.setTimeout(emulateKeyStroke, 2000 * playbackRate);
} else {
timeoutId = window.setTimeout(emulateKeyStroke, 100 * playbackRate);
}
setState({
characterIndex,
stringIndex,
});
};
useEffect(() => {
emulateKeyStroke();
return () => {
window.clearTimeout(timeoutId);
};
}, []);
const nonBreakingSpace = "\u00A0";
return strings[stringIndex].slice(0, characterIndex + 1) || nonBreakingSpace;
};

View File

@ -1,6 +1,6 @@
import { createSlice } from "@reduxjs/toolkit";
type Message = {
export type Message = {
content: string;
sender: "user" | "assistant";
};