Skip to content

Commit

Permalink
refactor: Xpert improvements
Browse files Browse the repository at this point in the history
This commit makes a number of improvements to the Xpert code. These improvements include moving the API call logic from Xpert widget to Sidebar component to make it easier to follow how an API call is made and to avoid side effects, moving the API loading state to the Redux store to have a cleaner data flow and to make the data available to the entire component tree, and correctly scrolling to the bottom of the ChatBox when the Sidebar is opened.
  • Loading branch information
MichaelRoytman committed Aug 21, 2023
1 parent 57987df commit c219134
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 26 deletions.
17 changes: 5 additions & 12 deletions src/components/ChatBox/index.jsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import React, { useEffect, useState } from 'react';
import React from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import Message from '../Message';
import './ChatBox.scss';

// container for all of the messages
const ChatBox = ({ messageList, chatboxContainerRef }) => {
const [isResponseLoading, setResponseLoading] = useState(false);

useEffect(() => {
if (messageList[messageList.length - 1]?.role === 'user') {
setResponseLoading(true);
} else {
setResponseLoading(false);
}
}, [messageList]);
const ChatBox = ({ chatboxContainerRef }) => {
const { messageList, apiIsLoading } = useSelector(state => state.learningAssistant);

return (
<div ref={chatboxContainerRef} className="scroller d-flex flex-column">
{messageList.map(({ role, content, timestamp }) => (
<Message key={timestamp.toString()} variant={role} message={content} timestamp={timestamp} />
))}
{isResponseLoading && (
{apiIsLoading && (
<div className="loading">Xpert is thinking</div>
)}
</div>
Expand Down
6 changes: 5 additions & 1 deletion src/components/Sidebar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import './Sidebar.scss';
import {
addChatMessage,
clearMessages,
getChatResponse,
updateCurrentMessage,
} from '../../data/thunks';
import { ReactComponent as NewXeySvg } from '../../assets/new_xey.svg';

const Sidebar = ({
courseId,
isOpen,
setIsOpen,
}) => {
Expand Down Expand Up @@ -51,7 +53,7 @@ const Sidebar = ({
requestAnimationFrame(scroll);
}
}
}, [messageList]);
}, [messageList, isOpen]);

const handleClick = () => {
setIsOpen(false);
Expand All @@ -65,6 +67,7 @@ const Sidebar = ({
event.preventDefault();
if (currentMessage) {
dispatch(addChatMessage('user', currentMessage));
dispatch(getChatResponse(courseId));
}
};

Expand Down Expand Up @@ -134,6 +137,7 @@ const Sidebar = ({
};

Sidebar.propTypes = {
courseId: PropTypes.string.isRequired,
isOpen: PropTypes.bool.isRequired,
setIsOpen: PropTypes.func.isRequired,
};
Expand Down
5 changes: 5 additions & 0 deletions src/data/slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const learningAssistantSlice = createSlice({
currentMessage: '',
messageList: [],
apiError: false,
apiIsLoading: false,
conversationId: uuidv4(),
},
reducers: {
Expand All @@ -28,6 +29,9 @@ export const learningAssistantSlice = createSlice({
setApiError: (state) => {
state.apiError = true;
},
setApiIsLoading: (state, { payload }) => {
state.apiIsLoading = payload;
},
},
});

Expand All @@ -37,6 +41,7 @@ export const {
setMessageList,
resetMessages,
setApiError,
setApiIsLoading,
} = learningAssistantSlice.actions;

export const {
Expand Down
7 changes: 6 additions & 1 deletion src/data/thunks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import fetchChatResponse from './api';
import {
setCurrentMessage,
clearCurrentMessage,
resetMessages,
setMessageList,
setApiError,
resetMessages,
setApiIsLoading,
} from './slice';

export function addChatMessage(role, content) {
Expand Down Expand Up @@ -36,11 +37,15 @@ export function addChatMessage(role, content) {
export function getChatResponse(courseId) {
return async (dispatch, getState) => {
const { messageList } = getState().learningAssistant;

dispatch(setApiIsLoading(true));
try {
const message = await fetchChatResponse(courseId, messageList);
dispatch(setApiIsLoading(false));
dispatch(addChatMessage(message.role, message.content));
} catch (error) {
dispatch(setApiError());
dispatch(setApiIsLoading(false));
}
};
}
Expand Down
19 changes: 7 additions & 12 deletions src/widgets/Xpert.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import PropTypes from 'prop-types';
import { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useState } from 'react';
import ToggleXpert from '../components/ToggleXpertButton';
import Sidebar from '../components/Sidebar';
import { getChatResponse } from '../data/thunks';

const Xpert = ({ courseId }) => {
const { messageList } = useSelector(state => state.learningAssistant);
const [sidebarIsOpen, setSidebarIsOpen] = useState(false);
const dispatch = useDispatch();

useEffect(() => {
if (messageList[messageList.length - 1]?.role === 'user') {
dispatch(getChatResponse(courseId));
}
}, [dispatch, courseId, messageList]);

return (
<div>
<ToggleXpert isOpen={sidebarIsOpen} setIsOpen={setSidebarIsOpen} courseId={courseId} />
<ToggleXpert
courseId={courseId}
isOpen={sidebarIsOpen}
setIsOpen={setSidebarIsOpen}
/>
<Sidebar
courseId={courseId}
isOpen={sidebarIsOpen}
setIsOpen={setSidebarIsOpen}
/>
Expand Down

0 comments on commit c219134

Please sign in to comment.