diff --git a/main.wasp b/main.wasp index 836cfa6..f63183d 100644 --- a/main.wasp +++ b/main.wasp @@ -66,7 +66,7 @@ app chatApp { ("node-fetch", "3.3.0"), ("react-hook-form", "^7.45.4"), ("stripe", "11.15.0"), - ("react-markdown", "9.0.0"), + ("markdown-to-jsx", "7.3.2"), ], } diff --git a/src/client/ChatPage.tsx b/src/client/ChatPage.tsx index 6c0cf39..ce6ed2e 100644 --- a/src/client/ChatPage.tsx +++ b/src/client/ChatPage.tsx @@ -1,8 +1,5 @@ -import { useState, useEffect, useRef } from "react"; - import { useQuery } from "@wasp/queries"; import getChats from "@wasp/queries/getChats"; -import type { Chat } from "@wasp/entities"; import CreateNewChatBtn from "./components/CreateNewChat"; import ChatsList from "./components/ChatList"; diff --git a/src/client/components/ConversationList.tsx b/src/client/components/ConversationList.tsx index 63deb36..508b930 100644 --- a/src/client/components/ConversationList.tsx +++ b/src/client/components/ConversationList.tsx @@ -1,4 +1,4 @@ -import Markdown from "react-markdown"; +import Markdown from "markdown-to-jsx"; import type { Conversation } from "@wasp/entities"; diff --git a/src/client/components/ConversationWrapper.tsx b/src/client/components/ConversationWrapper.tsx index 6b27191..a1d7b2a 100644 --- a/src/client/components/ConversationWrapper.tsx +++ b/src/client/components/ConversationWrapper.tsx @@ -1,6 +1,7 @@ -import { useState, useRef } from "react"; +import React from "react"; +import { useState, useRef, useEffect } from "react"; import { useParams } from "react-router"; -import { Redirect } from "react-router-dom"; +import { Redirect, useLocation } from "react-router-dom"; import { useQuery } from "@wasp/queries"; import updateConversation from "@wasp/actions/updateConversation"; @@ -12,14 +13,41 @@ import getConversations from "@wasp/queries/getConversations"; import ConversationsList from "./ConversationList"; import Loader from "./Loader"; -export default function ConversationWrapper() { - const [isLoading, setIsLoading] = useState(false); - const chatContainerRef = useRef(null); +// A custom hook that builds on useLocation to parse +// the query string for you. +function getQueryParam(paramName: string) { + const { search } = useLocation(); + return React.useMemo(() => new URLSearchParams(search), [search]).get( + paramName + ); +} +export async function triggerSubmit( + loginMsgQuery: string, // Todo: remove the below ignore comment // @ts-ignore - const { id } = useParams(); + submitBtnRef, + // Todo: remove the below ignore comment + // @ts-ignore + formInputRef +) { + if (loginMsgQuery) { + setTimeout(() => { + formInputRef.current.value = decodeURIComponent(loginMsgQuery); + submitBtnRef.current.click(); + }, 1500); // todo: remove timeout and implement a proper fix + } +} +export default function ConversationWrapper() { + // Todo: remove the below ignore comment + // @ts-ignore + const { id } = useParams(); + const [isLoading, setIsLoading] = useState(false); + const loginMsgQuery = getQueryParam("msg"); + const chatContainerRef = useRef(null); + const submitBtnRef = useRef(null); + const formInputRef = useRef(null); const { data: conversations, isLoading: isConversationLoading, @@ -32,21 +60,26 @@ export default function ConversationWrapper() { { enabled: !!id } ); - const chatContainerClass = `flex h-full flex-col items-center justify-between pb-24 overflow-y-auto bg-captn-light-blue ${ - isLoading ? "opacity-40" : "opacity-100" - }`; + // componentDidMount + useEffect(() => { + // Todo: remove the below ignore comment + // @ts-ignore + triggerSubmit(loginMsgQuery, submitBtnRef, formInputRef); + }, [loginMsgQuery]); - const handleFormSubmit = async (event: React.FormEvent) => { - event.preventDefault(); - try { - const target = event.target; - // Todo: remove the below ignore comment - // @ts-ignore - const userQuery = target.userQuery.value; + useEffect(() => { + if (chatContainerRef.current) { // Todo: remove the below ignore comment // @ts-ignore - target.reset(); + chatContainerRef.current.scrollTop = + // Todo: remove the below ignore comment + // @ts-ignore + chatContainerRef.current.scrollHeight; + } + }, [conversations]); + async function callAgent(userQuery: string) { + try { // 1. add new conversation to table const payload = { conversation_id: conversations.id, @@ -65,7 +98,6 @@ export default function ConversationWrapper() { message: userQuery, conv_id: payload.conversation_id, }); - setIsLoading(false); // 3. add agent response as new conversation in the table const openAIPayload = { conversation_id: conversations.id, @@ -77,16 +109,27 @@ export default function ConversationWrapper() { ], }; await updateConversation(openAIPayload); + setIsLoading(false); } catch (err: any) { setIsLoading(false); window.alert("Error: " + err.message); } + } + + const handleFormSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + const target = event.target; + // Todo: remove the below ignore comment + // @ts-ignore + const userQuery = target.userQuery.value; + // Todo: remove the below ignore comment + // @ts-ignore + target.reset(); + callAgent(userQuery); }; if (isConversationLoading && !!id) return ; if (isConversationError) { - console.log("Unable to load conversation."); - return ( <> @@ -94,6 +137,10 @@ export default function ConversationWrapper() { ); } + const chatContainerClass = `flex h-full flex-col items-center justify-between pb-24 overflow-y-auto bg-captn-light-blue ${ + isLoading ? "opacity-40" : "opacity-100" + }`; + return (
@@ -128,8 +175,10 @@ export default function ConversationWrapper() { className="block w-full p-4 pl-5 text-sm text-captn-light-cream border border-gray-300 rounded-lg bg-captn-dark-blue focus:ring-blue-500 focus:border-blue-500 dark:bg-captn-dark-blue dark:border-gray-600 dark:placeholder-gray-400 dark:text-captn-light-cream dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Send a message" required + ref={formInputRef} />