diff --git a/frontend/src/Navigation.tsx b/frontend/src/Navigation.tsx index c85b87bd..c3bf0a93 100644 --- a/frontend/src/Navigation.tsx +++ b/frontend/src/Navigation.tsx @@ -97,7 +97,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { fetchConversations(); } }, [conversations, dispatch]); - async function fetchConversations() { return await getConversations() .then((fetchedConversations) => { diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index b541c873..6813ed0f 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -4,7 +4,7 @@ import { useDarkTheme } from '../hooks'; import Hero from '../Hero'; import { AppDispatch } from '../store'; import ConversationBubble from './ConversationBubble'; -import { +import conversationSlice, { addQuery, fetchAnswer, selectQueries, @@ -17,16 +17,17 @@ import Spinner from './../assets/spinner.svg'; import { FEEDBACK, Query } from './conversationModels'; import { sendFeedback } from './conversationApi'; import ArrowDown from './../assets/arrow-down.svg'; - +import { selectConversationId } from '../preferences/preferenceSlice'; export default function Conversation() { const queries = useSelector(selectQueries); const status = useSelector(selectStatus); + const conversationId = useSelector(selectConversationId) const dispatch = useDispatch(); const endMessageRef = useRef(null); const inputRef = useRef(null); - const [isDarkTheme]= useDarkTheme(); + const [isDarkTheme] = useDarkTheme(); const [hasScrolledToLast, setHasScrolledToLast] = useState(true); - + const fetchStream = useRef(null) useEffect(() => { scrollIntoView(); }, [queries.length, queries[queries.length - 1]]); @@ -38,6 +39,11 @@ export default function Conversation() { } }, []); + useEffect(() => { + return () => { + fetchStream.current && fetchStream.current.abort(); //abort previous stream + } + }, [conversationId]) useEffect(() => { const observerCallback: IntersectionObserverCallback = (entries) => { entries.forEach((entry) => { @@ -69,9 +75,9 @@ export default function Conversation() { question = question.trim(); if (question === '') return; dispatch(addQuery({ prompt: question })); - dispatch(fetchAnswer({ question })); - }; + fetchStream.current = dispatch(fetchAnswer({ question })); + }; const handleFeedback = (query: Query, feedback: FEEDBACK, index: number) => { const prevFeedback = query.feedback; dispatch(updateQuery({ index, query: { feedback } })); diff --git a/frontend/src/conversation/conversationApi.ts b/frontend/src/conversation/conversationApi.ts index a01d034a..b446fc17 100644 --- a/frontend/src/conversation/conversationApi.ts +++ b/frontend/src/conversation/conversationApi.ts @@ -5,6 +5,7 @@ const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; export function fetchAnswerApi( question: string, + signal:AbortSignal, apiKey: string, selectedDocs: Doc, history: Array = [], @@ -65,6 +66,7 @@ export function fetchAnswerApi( conversation_id: conversationId, prompt_id: promptId, }), + signal, }) .then((response) => { if (response.ok) { @@ -87,6 +89,7 @@ export function fetchAnswerApi( export function fetchAnswerSteaming( question: string, + signal:AbortSignal, apiKey: string, selectedDocs: Doc, history: Array = [], @@ -134,6 +137,7 @@ export function fetchAnswerSteaming( 'Content-Type': 'application/json', }, body: JSON.stringify(body), + signal }) .then((response) => { if (!response.body) throw Error('No response body'); diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 3f840d3c..dd21c152 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -16,17 +16,19 @@ const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true'; export const fetchAnswer = createAsyncThunk( 'fetchAnswer', - async ({ question }, { dispatch, getState }) => { + async ({ question }, { dispatch, getState, signal }) => { const state = getState() as RootState; if (state.preference) { if (API_STREAMING) { await fetchAnswerSteaming( question, + signal, state.preference.apiKey, state.preference.selectedDocs!, state.conversation.queries, state.conversation.conversationId, state.preference.prompt.id, + (event) => { const data = JSON.parse(event.data); @@ -78,6 +80,7 @@ export const fetchAnswer = createAsyncThunk( } else { const answer = await fetchAnswerApi( question, + signal, state.preference.apiKey, state.preference.selectedDocs!, state.conversation.queries, @@ -193,6 +196,11 @@ export const conversationSlice = createSlice({ state.status = 'loading'; }) .addCase(fetchAnswer.rejected, (state, action) => { + if(action.meta.aborted) + { + state.status = 'idle'; + return state; + } state.status = 'failed'; state.queries[state.queries.length - 1].error = 'Something went wrong. Please try again later.'; diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts index a2bf3f3f..8327f766 100644 --- a/frontend/src/hooks/index.ts +++ b/frontend/src/hooks/index.ts @@ -70,7 +70,7 @@ export function useDarkTheme() { useEffect(() => { // Check if dark mode preference exists in local storage - const savedMode:string | null = localStorage.getItem('selectedTheme'); + const savedMode: string | null = localStorage.getItem('selectedTheme'); // Set dark mode based on local storage preference if (savedMode === 'Dark') { @@ -83,22 +83,19 @@ export function useDarkTheme() { document.documentElement.classList.remove('dark'); } }, []); - useEffect(()=>{ - localStorage.setItem('selectedTheme',isDarkTheme ? 'Dark' : 'Light'); - if(isDarkTheme){ + useEffect(() => { + localStorage.setItem('selectedTheme', isDarkTheme ? 'Dark' : 'Light'); + if (isDarkTheme) { document.documentElement.classList.add('dark'); document.documentElement.classList.add('dark:bg-raisin-black'); } - else{ + else { document.documentElement.classList.remove('dark'); } - }) - - // Function to toggle dark mode - const toggleTheme:any = () => { + }, [isDarkTheme]) + //method to toggle theme + const toggleTheme: any = () => { setIsDarkTheme(!isDarkTheme) - }; - return [isDarkTheme, toggleTheme]; } \ No newline at end of file