Explorar el Código

refactor & fix frontend: typing chat (#861)

808vita hace 1 año
padre
commit
6f346b3789

+ 18 - 67
frontend/src/components/ChatInterface.tsx

@@ -8,9 +8,8 @@ import { useTypingEffect } from "../hooks/useTypingEffect";
 import { I18nKey } from "../i18n/declaration";
 import {
   addAssistantMessageToChat,
-  setCurrentQueueMarkerState,
-  setCurrentTypingMsgState,
   setTypingActive,
+  takeOneAndType,
 } from "../services/chatService";
 import { Message } from "../state/chatSlice";
 import { RootState } from "../store";
@@ -29,25 +28,21 @@ interface IChatBubbleProps {
  *
  */
 function TypingChat() {
-  const { currentTypingMessage, currentQueueMarker, queuedTyping, messages } =
-    useSelector((state: RootState) => state.chat);
+  const { typeThis } = useSelector((state: RootState) => state.chat);
 
-  const messageContent = useTypingEffect([currentTypingMessage], {
+  const messageContent = useTypingEffect([typeThis?.content], {
     loop: false,
     setTypingActive,
-    setCurrentQueueMarkerState,
-    currentQueueMarker,
-    playbackRate: 0.1,
+    playbackRate: 0.099,
     addAssistantMessageToChat,
-    assistantMessageObj: messages?.[queuedTyping[currentQueueMarker]],
+    takeOneAndType,
+    typeThis,
   });
 
   return (
-    currentQueueMarker !== null && (
-      <Card className="bg-success-100">
-        <CardBody>{messageContent}</CardBody>
-      </Card>
-    )
+    <Card className="bg-success-100">
+      <CardBody>{messageContent}</CardBody>
+    </Card>
   );
 }
 
@@ -72,14 +67,9 @@ function ChatBubble({ msg }: IChatBubbleProps): JSX.Element {
 
 function MessageList(): JSX.Element {
   const messagesEndRef = useRef<HTMLDivElement>(null);
-  const {
-    messages,
-    queuedTyping,
-    typingActive,
-    currentQueueMarker,
-    currentTypingMessage,
-    newChatSequence,
-  } = useSelector((state: RootState) => state.chat);
+  const { typingActive, newChatSequence, typeThis } = useSelector(
+    (state: RootState) => state.chat,
+  );
 
   const messageScroll = () => {
     messagesEndRef.current?.scrollIntoView({
@@ -101,56 +91,17 @@ function MessageList(): JSX.Element {
   }, [newChatSequence, typingActive]);
 
   useEffect(() => {
-    const newMessage = messages?.[queuedTyping[currentQueueMarker]]?.content;
-
-    if (
-      currentQueueMarker !== null &&
-      currentQueueMarker !== 0 &&
-      currentTypingMessage !== newMessage
-    ) {
-      setCurrentTypingMsgState(
-        messages?.[queuedTyping?.[currentQueueMarker]]?.content,
-      );
-    }
-  }, [queuedTyping]);
-
-  useEffect(() => {
-    if (currentTypingMessage === "") return;
+    if (typeThis.content === "") return;
 
     if (!typingActive) setTypingActive(true);
-  }, [currentTypingMessage]);
-
-  useEffect(() => {
-    const newMessage = messages?.[queuedTyping[currentQueueMarker]]?.content;
-    if (
-      newMessage &&
-      typingActive === false &&
-      currentTypingMessage !== newMessage
-    ) {
-      if (currentQueueMarker !== 0) {
-        setCurrentTypingMsgState(
-          messages?.[queuedTyping?.[currentQueueMarker]]?.content,
-        );
-      }
-    }
-  }, [typingActive]);
-
-  useEffect(() => {
-    if (currentQueueMarker === 0) {
-      setCurrentTypingMsgState(messages?.[queuedTyping?.[0]]?.content);
-    }
-  }, [currentQueueMarker]);
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [typeThis]);
 
   return (
     <div className="flex-1 overflow-y-auto">
-      {newChatSequence.map((msg, index) =>
-        // eslint-disable-next-line no-nested-ternary
-        msg.sender === "user" || msg.sender === "assistant" ? (
-          <ChatBubble key={index} msg={msg} />
-        ) : (
-          <div key={index} />
-        ),
-      )}
+      {newChatSequence.map((msg, index) => (
+        <ChatBubble key={index} msg={msg} />
+      ))}
 
       {typingActive && (
         <div className="flex mb-2.5 pr-5 pl-5 bg-s">

+ 9 - 10
frontend/src/hooks/useTypingEffect.ts

@@ -9,25 +9,23 @@ export const useTypingEffect = (
     loop = false,
     playbackRate = 0.1,
     setTypingActive = () => {},
-    setCurrentQueueMarkerState = () => {},
-    currentQueueMarker = 0,
     addAssistantMessageToChat = () => {},
-    assistantMessageObj = { content: "", sender: "assistant" },
+    takeOneAndType = () => {},
+    typeThis = { content: "", sender: "assistant" },
   }: {
     loop?: boolean;
     playbackRate?: number;
     setTypingActive?: (bool: boolean) => void;
-    setCurrentQueueMarkerState?: (marker: number) => void;
-    currentQueueMarker?: number;
     addAssistantMessageToChat?: (msg: Message) => void;
-    assistantMessageObj?: Message;
+    takeOneAndType?: () => void;
+    typeThis?: Message;
   } = {
     loop: false,
     playbackRate: 0.1,
     setTypingActive: () => {},
-    currentQueueMarker: 0,
     addAssistantMessageToChat: () => {},
-    assistantMessageObj: { content: "", sender: "assistant" },
+    takeOneAndType: () => {},
+    typeThis: { content: "", sender: "assistant" },
   },
 ) => {
   // eslint-disable-next-line prefer-const
@@ -50,8 +48,8 @@ export const useTypingEffect = (
       if (stringIndex === strings.length) {
         if (!loop) {
           setTypingActive(false);
-          setCurrentQueueMarkerState(currentQueueMarker + 1);
-          addAssistantMessageToChat(assistantMessageObj);
+          addAssistantMessageToChat(typeThis);
+          takeOneAndType();
           return;
         }
         stringIndex = 0;
@@ -73,6 +71,7 @@ export const useTypingEffect = (
     return () => {
       window.clearTimeout(timeoutId);
     };
+    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, []);
 
   const nonBreakingSpace = "\u00A0";

+ 4 - 14
frontend/src/services/chatService.ts

@@ -2,9 +2,7 @@ import {
   Message,
   appendToNewChatSequence,
   appendUserMessage,
-  emptyOutQueuedTyping,
-  setCurrentQueueMarker,
-  setCurrentTypingMessage,
+  takeOneTypeIt,
   toggleTypingActive,
 } from "../state/chatSlice";
 import Socket from "./socket";
@@ -39,17 +37,9 @@ export function sendChatMessageFromEvent(event: string | SocketMessage): void {
 export function setTypingActive(bool: boolean): void {
   store.dispatch(toggleTypingActive(bool));
 }
-
-export function resetQueuedTyping(): void {
-  store.dispatch(emptyOutQueuedTyping());
-}
-
-export function setCurrentTypingMsgState(msg: string): void {
-  store.dispatch(setCurrentTypingMessage(msg));
-}
-export function setCurrentQueueMarkerState(index: number): void {
-  store.dispatch(setCurrentQueueMarker(index));
-}
 export function addAssistantMessageToChat(msg: Message): void {
   store.dispatch(appendToNewChatSequence(msg));
 }
+export function takeOneAndType(): void {
+  store.dispatch(takeOneTypeIt());
+}

+ 30 - 25
frontend/src/state/chatSlice.ts

@@ -6,19 +6,16 @@ export type Message = {
 };
 
 const initialMessages: Message[] = [];
-const queuedMessages: number[] = [];
-const currentQueueMarker: number = 0;
 export const chatSlice = createSlice({
   name: "chat",
   initialState: {
     messages: initialMessages,
-    queuedTyping: queuedMessages,
     typingActive: false,
-    currentTypingMessage: "",
-    currentQueueMarker,
     userMessages: initialMessages,
     assistantMessages: initialMessages,
+    assistantMessagesTypingQueue: initialMessages,
     newChatSequence: initialMessages,
+    typeThis: { content: "", sender: "assistant" } as Message,
   },
   reducers: {
     appendUserMessage: (state, action) => {
@@ -28,30 +25,40 @@ export const chatSlice = createSlice({
     },
     appendAssistantMessage: (state, action) => {
       state.messages.push({ content: action.payload, sender: "assistant" });
-      state.assistantMessages.push({
-        content: action.payload,
-        sender: "assistant",
-      });
-      // state.queuedTyping.push(action.payload);
-      const assistantMessageIndex = state.messages.length - 1;
-      state.queuedTyping.push(assistantMessageIndex);
-    },
-    setCurrentQueueMarker: (state, action) => {
-      state.currentQueueMarker = action.payload;
+
+      if (
+        state.assistantMessagesTypingQueue.length > 0 ||
+        state.typingActive === true
+      ) {
+        state.assistantMessagesTypingQueue.push({
+          content: action.payload,
+          sender: "assistant",
+        });
+      } else if (
+        state.assistantMessagesTypingQueue.length === 0 &&
+        state.typingActive === false
+      ) {
+        state.typeThis = {
+          content: action.payload,
+          sender: "assistant",
+        };
+        state.typingActive = true;
+      }
     },
+
     toggleTypingActive: (state, action) => {
       state.typingActive = action.payload;
     },
-    emptyOutQueuedTyping: (state) => {
-      state.queuedTyping = [];
-    },
-    setCurrentTypingMessage: (state, action) => {
-      state.currentTypingMessage = action.payload;
-      // state.currentQueueMarker += 1;
-    },
+
     appendToNewChatSequence: (state, action) => {
       state.newChatSequence.push(action.payload);
     },
+
+    takeOneTypeIt: (state) => {
+      if (state.assistantMessagesTypingQueue.length > 0) {
+        state.typeThis = state.assistantMessagesTypingQueue.shift() as Message;
+      }
+    },
   },
 });
 
@@ -59,10 +66,8 @@ export const {
   appendUserMessage,
   appendAssistantMessage,
   toggleTypingActive,
-  emptyOutQueuedTyping,
-  setCurrentTypingMessage,
-  setCurrentQueueMarker,
   appendToNewChatSequence,
+  takeOneTypeIt,
 } = chatSlice.actions;
 
 export default chatSlice.reducer;