diff --git a/src/components/ChatBox/ChatBox.scss b/src/components/ChatBox/ChatBox.scss index 8403ca00..9a24f0c6 100644 --- a/src/components/ChatBox/ChatBox.scss +++ b/src/components/ChatBox/ChatBox.scss @@ -1,9 +1,14 @@ -.scroller { - overflow-y: scroll; - scrollbar-width: thin; - width: 100%; - opacity: 1; - margin-right: 0; +.xpert-chat-scroller { + position: relative; + flex: 1; + + .messages-list { + overflow-y: scroll; + scrollbar-width: thin; + position: absolute; + inset: 0; + padding: 1rem 0; + } &:after { content: ""; /* Add an empty content area after the chat messages */ @@ -11,20 +16,38 @@ height: 0; clear: both; } -} -.loading { - font-size: 13px; - padding-left: 10px; + .loading { + font-size: 13px; + padding-left: 10px; - &:after { - overflow: hidden; - display: inline-block; - vertical-align: bottom; - -webkit-animation: ellipsis steps(4,end) 900ms infinite; - animation: ellipsis steps(4,end) 900ms infinite; - content: "\2026"; /* ascii code for the ellipsis character */ - width: 0px; + &:after { + overflow: hidden; + display: inline-block; + vertical-align: bottom; + -webkit-animation: ellipsis steps(4,end) 900ms infinite; + animation: ellipsis steps(4,end) 900ms infinite; + content: "\2026"; /* ascii code for the ellipsis character */ + width: 0px; + } + } + + .separator { + position: absolute; + z-index: 100; + height: 5px; + padding: 5px; + opacity: 0.3; + + &--top { + inset: 0 0 auto 0; + background: linear-gradient(180deg, rgba(0, 0, 0, 0.35), transparent); + } + + &--bottom { + inset: auto 0 0 0; + background: linear-gradient(0, rgba(0, 0, 0, 0.35), transparent); + } } } diff --git a/src/components/ChatBox/index.jsx b/src/components/ChatBox/index.jsx index df2eec73..ce98fce7 100644 --- a/src/components/ChatBox/index.jsx +++ b/src/components/ChatBox/index.jsx @@ -1,6 +1,5 @@ -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import { useSelector } from 'react-redux'; -import PropTypes from 'prop-types'; import Message from '../Message'; import './ChatBox.scss'; import MessageDivider from '../MessageDivider'; @@ -14,34 +13,53 @@ function isToday(date) { ); } +const scrollToBottom = (containerRef, smooth = false) => { + setTimeout(() => { + containerRef.current.scrollTo({ + top: containerRef.current.scrollHeight, + behavior: smooth ? 'smooth' : 'instant', + }); + }); +}; + // container for all of the messages -const ChatBox = ({ chatboxContainerRef }) => { +const ChatBox = () => { + const firstRender = useRef(true); + const messageContainerRef = useRef(); + const { messageList, apiIsLoading } = useSelector(state => state.learningAssistant); const messagesBeforeToday = messageList.filter((m) => (!isToday(new Date(m.timestamp)))); const messagesToday = messageList.filter((m) => (isToday(new Date(m.timestamp)))); + useEffect(() => { + if (firstRender.current) { + scrollToBottom(messageContainerRef); + firstRender.current = false; + return; + } + + scrollToBottom(messageContainerRef, true); + }, [messageList.length]); + // message divider should not display if no messages or if all messages sent today. return ( -
- {messagesBeforeToday.map(({ role, content, timestamp }) => ( - - ))} - {(messageList.length !== 0 && messagesBeforeToday.length !== 0) && ()} - {messagesToday.map(({ role, content, timestamp }) => ( - - ))} - {apiIsLoading && ( +
+
+ {messagesBeforeToday.map(({ role, content, timestamp }) => ( + + ))} + {(messageList.length !== 0 && messagesBeforeToday.length !== 0) && ()} + {messagesToday.map(({ role, content, timestamp }) => ( + + ))} + {apiIsLoading && (
Xpert is thinking
- )} + )} +
+ +
); }; -ChatBox.propTypes = { - chatboxContainerRef: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.shape({ current: PropTypes.instanceOf(Element) }), - ]).isRequired, -}; - export default ChatBox; diff --git a/src/components/Message/index.jsx b/src/components/Message/index.jsx index dda2e725..16180eaa 100644 --- a/src/components/Message/index.jsx +++ b/src/components/Message/index.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; const Message = ({ variant, message }) => (
diff --git a/src/components/MessageForm/MessageForm.scss b/src/components/MessageForm/MessageForm.scss index b58e1e39..b309cd03 100644 --- a/src/components/MessageForm/MessageForm.scss +++ b/src/components/MessageForm/MessageForm.scss @@ -1,4 +1,6 @@ .message-form { + padding: 0.75rem 1.5rem; + .send-message-input { .pgn__form-control-floating-label { color: #ADADAD; @@ -6,6 +8,15 @@ input { border-radius: 1rem; + border: 1px solid #C8C8CC; } } + + .pgn__form-control-decorator-group { + margin-inline-end: 0; + } + + button { + color: #8F8F8F; + } } diff --git a/src/components/MessageForm/index.jsx b/src/components/MessageForm/index.jsx index cae5616f..ef96649d 100644 --- a/src/components/MessageForm/index.jsx +++ b/src/components/MessageForm/index.jsx @@ -61,18 +61,16 @@ const MessageForm = ({ courseId, shouldAutofocus, unitId }) => { return (
- - - + ); }; diff --git a/src/components/Sidebar/Sidebar.scss b/src/components/Sidebar/Sidebar.scss index 8d385146..53880460 100644 --- a/src/components/Sidebar/Sidebar.scss +++ b/src/components/Sidebar/Sidebar.scss @@ -9,8 +9,6 @@ background-color: white; width: 100%; max-width: 25rem; - /* Add smooth scrolling behavior */ - scroll-behavior: smooth; h1 { font-size: 1.25rem; @@ -18,32 +16,38 @@ } button.chat-close { - top: 0; - right: 0; + top: 0.75rem; + right: 1.5rem; + height: 1.5rem; + width: 1.5rem; + + .btn-icon__icon { + width: 1.375rem !important; + height: 1.375rem !important; + } } .sidebar-header { + display: flex; + align-items: center; background: linear-gradient(to left, rgb(58, 101, 108) 0px, rgb(0, 29, 34)); width: 100%; - height: 60px; + height: 48px; + padding: 0 1.5rem; + svg { - height: 30px; + display: block; + height: 24px; } } + .sidebar-footer { + + } + .trial-header { font-size: 0.9em; background-color: #F49974; } } - -.separator { - z-index: 100; - width: 100%; - height: 5px; - padding: 5px; - background: -webkit-linear-gradient(270deg, rgba(0, 0, 0, 0.35), transparent); - background: linear-gradient(180deg, rgba(0, 0, 0, 0.35), transparent); - opacity: 0.3; -} diff --git a/src/components/Sidebar/index.jsx b/src/components/Sidebar/index.jsx index 5f6425d8..f8cb8e20 100644 --- a/src/components/Sidebar/index.jsx +++ b/src/components/Sidebar/index.jsx @@ -1,4 +1,4 @@ -import React, { useRef, useEffect } from 'react'; +import React from 'react'; import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; @@ -37,42 +37,6 @@ const Sidebar = ({ const { track } = useTrackEvent(); - const chatboxContainerRef = useRef(null); - - // this use effect is intended to scroll to the bottom of the chat window, in the case - // that a message is larger than the chat window height. - useEffect(() => { - const messageContainer = chatboxContainerRef.current; - - if (messageContainer) { - const { scrollHeight, clientHeight } = messageContainer; - const maxScrollTop = scrollHeight - clientHeight; - const duration = 200; - - if (maxScrollTop > 0) { - const startTime = Date.now(); - const endTime = startTime + duration; - - const scroll = () => { - const currentTime = Date.now(); - const timeFraction = (currentTime - startTime) / duration; - const scrollTop = maxScrollTop * timeFraction; - - messageContainer.scrollTo({ - top: scrollTop, - behavior: 'smooth', - }); - - if (currentTime < endTime) { - requestAnimationFrame(scroll); - } - }; - - requestAnimationFrame(scroll); - } - } - }, [messageList, isOpen, apiError]); - const handleClick = () => { setIsOpen(false); @@ -120,10 +84,10 @@ const Sidebar = ({ const getSidebar = () => (
-
+
{upgradeable @@ -132,11 +96,7 @@ const Sidebar = ({ {getDaysRemainingMessage()}
)} - - + { apiError && ( @@ -145,7 +105,9 @@ const Sidebar = ({
) } - {getMessageForm()} +
+ {getMessageForm()} +
); @@ -165,7 +127,7 @@ const Sidebar = ({ data-testid="sidebar" >