Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
nygrenh committed Sep 30, 2024
1 parent e491e5d commit 95118f2
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { css, keyframes } from "@emotion/css"
import { useQuery } from "@tanstack/react-query"
import { useEffect, useState } from "react"
import React, { useEffect, useState } from "react"

import ChatbotDialogBody from "./ChatbotDialogBody"
import ChatbotDialogHeader from "./ChatbotDialogHeader"
Expand Down Expand Up @@ -39,6 +39,8 @@ const ChatbotDialog: React.FC<ChatbotDialogProps> = (props) => {
const { dialogOpen, chatbotConfigurationId } = props
const [shouldRender, setShouldRender] = useState(dialogOpen)

const [error, setError] = useState<Error | null>(null)

const currentConversationInfoQuery = useQuery({
queryKey: ["currentConversationInfo", chatbotConfigurationId],
queryFn: () => getChatbotCurrentConversationInfo(chatbotConfigurationId),
Expand Down Expand Up @@ -83,7 +85,12 @@ const ChatbotDialog: React.FC<ChatbotDialogProps> = (props) => {
onAnimationEnd={handleAnimationEnd}
>
<ChatbotDialogHeader {...props} currentConversationInfo={currentConversationInfoQuery} />
<ChatbotDialogBody {...props} currentConversationInfo={currentConversationInfoQuery} />
<ChatbotDialogBody
{...props}
currentConversationInfo={currentConversationInfoQuery}
error={error}
setError={setError}
/>
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import Spinner from "@/shared-module/common/components/Spinner"
import useToastMutation from "@/shared-module/common/hooks/useToastMutation"
import { baseTheme } from "@/shared-module/common/styles"

interface ChatbotDialogBodyProps extends ChatbotDialogProps {
currentConversationInfo: UseQueryResult<ChatbotConversationInfo, Error>
error: Error | null
setError: (error: Error | null) => void
}

interface MessageState {
optimisticMessage: string | null
streamingMessage: string | null
Expand All @@ -39,9 +45,12 @@ const messageReducer = (state: MessageState, action: MessageAction): MessageStat
}
}

const ChatbotDialogBody: React.FC<
ChatbotDialogProps & { currentConversationInfo: UseQueryResult<ChatbotConversationInfo, Error> }
> = ({ currentConversationInfo, chatbotConfigurationId }) => {
const ChatbotDialogBody: React.FC<ChatbotDialogBodyProps> = ({
currentConversationInfo,
chatbotConfigurationId,
error,
setError,
}) => {
const scrollContainerRef = useRef<HTMLDivElement>(null)
const { t } = useTranslation()

Expand All @@ -58,6 +67,7 @@ const ChatbotDialogBody: React.FC<
onSuccess: () => {
currentConversationInfo.refetch()
dispatch({ type: "RESET_MESSAGES" })
setError(null) // Clear any existing errors when starting a new conversation
},
},
)
Expand Down Expand Up @@ -106,6 +116,17 @@ const ChatbotDialogBody: React.FC<
onSuccess: async () => {
await currentConversationInfo.refetch()
dispatch({ type: "RESET_MESSAGES" })
setError(null)
},
onError: async (error) => {
if (error instanceof Error) {
setError(error)
dispatch({ type: "SET_OPTIMISTIC_MESSAGE", payload: null })
} else {
console.error(`Failed to send chat message: ${error}`)
setError(new Error("Unknown error occurred"))
}
await currentConversationInfo.refetch()
},
},
)
Expand Down Expand Up @@ -179,11 +200,7 @@ const ChatbotDialogBody: React.FC<
`}
>
<ErrorBanner error={currentConversationInfo.error} variant="readOnly" />
<Button
onClick={() => currentConversationInfo.refetch()}
variant="secondary"
size={"small"}
>
<Button onClick={() => currentConversationInfo.refetch()} variant="secondary" size="small">
{t("try-again")}
</Button>
</div>
Expand Down Expand Up @@ -273,6 +290,19 @@ const ChatbotDialogBody: React.FC<
/>
))}
</div>
{error && (
<div
className={css`
padding: 10px;
background-color: ${baseTheme.colors.red[100]};
color: ${baseTheme.colors.red[500]};
margin: 10px;
border-radius: 5px;
`}
>
{t("failed-to-send-message")}: {error.message}
</div>
)}
<div
className={css`
display: flex;
Expand Down
23 changes: 10 additions & 13 deletions services/course-material/src/components/chatbot/MessageBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,17 @@ const bubbleStyle = (isFromChatbot: boolean) => css`
margin-left: 2rem;
align-self: flex-end;
border: 2px solid ${baseTheme.colors.gray[200]};
background-color: #ffffff;
`}
`

const MessageBubble: React.FC<MessageBubbleProps> = React.memo(
({ message, isFromChatbot, isPending }) => {
return (
<div className={bubbleStyle(isFromChatbot)}>
<span>{message}</span>
{isPending && <ThinkingIndicator />}
</div>
)
},
)

MessageBubble.displayName = "MessageBubble"
const MessageBubble: React.FC<MessageBubbleProps> = ({ message, isFromChatbot, isPending }) => {
return (
<div className={bubbleStyle(isFromChatbot)}>
<span>{message}</span>
{isPending && <ThinkingIndicator />}
</div>
)
}

export default MessageBubble
export default React.memo(MessageBubble)
31 changes: 22 additions & 9 deletions services/course-material/src/services/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,16 +698,29 @@ export const sendChatbotMessage = async (
conversationId: string,
message: string,
): Promise<ReadableStream<Uint8Array>> => {
const res = await fetch(
`/api/v0/course-material/chatbot/${chatBotConfigurationId}/conversations/${conversationId}/send-message`,
{
method: "POST",
body: JSON.stringify(message),
headers: {
"Content-Type": "application/json",
},
const url = `/api/v0/course-material/chatbot/${chatBotConfigurationId}/conversations/${conversationId}/send-message`

const requestOptions: RequestInit = {
method: "POST",
body: JSON.stringify({ message }), // It's better to send an object for clarity
headers: {
"Content-Type": "application/json",
},
)
}

const res = await fetch(url, requestOptions)

if (res.status !== 200) {
let errorDetails: string
try {
const errorData = await res.json()
errorDetails = JSON.stringify(errorData)
} catch {
errorDetails = await res.text()
}

throw new Error(`Request failed with status ${res.status}: ${errorDetails}`)
}

const stream = res.body

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"exercises-attempted": "Exercises attempted",
"exercises-in-this-chapter": "Exercises in this chapter",
"exit": "Exit",
"failed-to-send-message": "Failed to send message",
"failed-to-submit": "Failed to submit {{ error }}",
"feedback-submitted-succesfully": "Feedback submitted successfully",
"finnish": "Finnish",
Expand Down

0 comments on commit 95118f2

Please sign in to comment.