Autoscroll to end of chat fix (#201)

bug fix for autoscrolling on message send/recieve
This commit is contained in:
Sean Hatfield 2023-08-21 15:46:31 -07:00 committed by GitHub
parent 4a67cf2198
commit b01e49bb3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 124 additions and 144 deletions

View File

@ -1,23 +1,12 @@
import { useEffect, useRef, memo } from "react"; import { memo, forwardRef } from "react";
import { AlertTriangle } from "react-feather"; import { AlertTriangle } from "react-feather";
import Jazzicon from "../../../../UserIcon"; import Jazzicon from "../../../../UserIcon";
import renderMarkdown from "../../../../../utils/chat/markdown"; import renderMarkdown from "../../../../../utils/chat/markdown";
import { userFromStorage } from "../../../../../utils/request"; import { userFromStorage } from "../../../../../utils/request";
import Citations from "../Citation"; import Citations from "../Citation";
function HistoricalMessage({ const HistoricalMessage = forwardRef(
message, ({ message, role, workspace, sources = [], error = false }, ref) => {
role,
workspace,
sources = [],
error = false,
}) {
const replyRef = useRef(null);
useEffect(() => {
if (replyRef.current)
replyRef.current.scrollIntoView({ behavior: "smooth", block: "end" });
}, [replyRef.current]);
if (role === "user") { if (role === "user") {
return ( return (
<div className="flex justify-end mb-4 items-start"> <div className="flex justify-end mb-4 items-start">
@ -50,7 +39,7 @@ function HistoricalMessage({
} }
return ( return (
<div ref={replyRef} className="flex justify-start items-end mb-4"> <div ref={ref} className="flex justify-start items-end mb-4">
<Jazzicon size={30} user={{ uid: workspace.slug }} /> <Jazzicon size={30} user={{ uid: workspace.slug }} />
<div className="ml-2 py-3 px-4 overflow-x-scroll w-fit md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm"> <div className="ml-2 py-3 px-4 overflow-x-scroll w-fit md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm">
<span <span
@ -61,6 +50,7 @@ function HistoricalMessage({
</div> </div>
</div> </div>
); );
} }
);
export default memo(HistoricalMessage); export default memo(HistoricalMessage);

View File

@ -1,28 +1,21 @@
import { memo, useEffect, useRef } from "react"; import { forwardRef, memo } from "react";
import { AlertTriangle } from "react-feather"; import { AlertTriangle } from "react-feather";
import Jazzicon from "../../../../UserIcon"; import Jazzicon from "../../../../UserIcon";
import renderMarkdown from "../../../../../utils/chat/markdown"; import renderMarkdown from "../../../../../utils/chat/markdown";
import Citations from "../Citation"; import Citations from "../Citation";
function PromptReply({ const PromptReply = forwardRef(
uuid, (
reply, { uuid, reply, pending, error, workspace, sources = [], closed = true },
pending, ref
error, ) => {
workspace,
sources = [],
closed = true,
}) {
const replyRef = useRef(null);
useEffect(() => {
if (replyRef.current)
replyRef.current.scrollIntoView({ behavior: "smooth", block: "end" });
}, [replyRef.current]);
if (!reply && !sources.length === 0 && !pending && !error) return null; if (!reply && !sources.length === 0 && !pending && !error) return null;
if (pending) { if (pending) {
return ( return (
<div className="chat__message flex justify-start mb-4 items-end"> <div
ref={ref}
className="chat__message flex justify-start mb-4 items-end"
>
<Jazzicon size={30} user={{ uid: workspace.slug }} /> <Jazzicon size={30} user={{ uid: workspace.slug }} />
<div className="ml-2 pt-2 px-6 w-fit md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm"> <div className="ml-2 pt-2 px-6 w-fit md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm">
<span className={`inline-block p-2`}> <span className={`inline-block p-2`}>
@ -40,8 +33,8 @@ function PromptReply({
<div className="ml-2 py-3 px-4 rounded-br-3xl rounded-tr-3xl rounded-tl-xl text-slate-100 "> <div className="ml-2 py-3 px-4 rounded-br-3xl rounded-tr-3xl rounded-tl-xl text-slate-100 ">
<div className="bg-red-50 text-red-500 rounded-lg w-fit flex flex-col p-2"> <div className="bg-red-50 text-red-500 rounded-lg w-fit flex flex-col p-2">
<span className={`inline-block`}> <span className={`inline-block`}>
<AlertTriangle className="h-4 w-4 mb-1 inline-block" /> Could not <AlertTriangle className="h-4 w-4 mb-1 inline-block" /> Could
respond to message. not respond to message.
</span> </span>
<span className="text-xs">Reason: {error || "unknown"}</span> <span className="text-xs">Reason: {error || "unknown"}</span>
</div> </div>
@ -51,11 +44,7 @@ function PromptReply({
} }
return ( return (
<div <div key={uuid} ref={ref} className="mb-4 flex justify-start items-end">
key={uuid}
ref={replyRef}
className="mb-4 flex justify-start items-end"
>
<Jazzicon size={30} user={{ uid: workspace.slug }} /> <Jazzicon size={30} user={{ uid: workspace.slug }} />
<div className="ml-2 py-3 px-4 overflow-x-scroll w-fit md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm"> <div className="ml-2 py-3 px-4 overflow-x-scroll w-fit md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm">
<span <span
@ -66,6 +55,7 @@ function PromptReply({
</div> </div>
</div> </div>
); );
} }
);
export default memo(PromptReply); export default memo(PromptReply);

View File

@ -1,8 +1,19 @@
import { Frown } from "react-feather"; import { Frown } from "react-feather";
import HistoricalMessage from "./HistoricalMessage"; import HistoricalMessage from "./HistoricalMessage";
import PromptReply from "./PromptReply"; import PromptReply from "./PromptReply";
import { useEffect, useRef } from "react";
export default function ChatHistory({ history = [], workspace }) { export default function ChatHistory({ history = [], workspace }) {
const replyRef = useRef(null);
useEffect(() => {
if (replyRef.current) {
setTimeout(() => {
replyRef.current.scrollIntoView({ behavior: "smooth", block: "end" });
}, 700);
}
}, [history]);
if (history.length === 0) { if (history.length === 0) {
return ( return (
<div className="flex flex-col h-[89%] md:mt-0 pb-5 w-full justify-center items-center"> <div className="flex flex-col h-[89%] md:mt-0 pb-5 w-full justify-center items-center">
@ -22,48 +33,37 @@ export default function ChatHistory({ history = [], workspace }) {
className="h-[89%] pb-[100px] md:pt-[50px] md:pt-0 md:pb-5 mx-2 md:mx-0 overflow-y-scroll flex flex-col justify-start no-scroll" className="h-[89%] pb-[100px] md:pt-[50px] md:pt-0 md:pb-5 mx-2 md:mx-0 overflow-y-scroll flex flex-col justify-start no-scroll"
id="chat-history" id="chat-history"
> >
{history.map( {history.map((props, index) => {
( const isLastMessage = index === history.length - 1;
{
uuid = null, if (props.role === "assistant" && props.animate) {
content,
sources = [],
role,
closed = true,
pending = false,
error = false,
animate = false,
},
index
) => {
const isLastBotReply =
index === history.length - 1 && role === "assistant";
if (isLastBotReply && animate) {
return ( return (
<PromptReply <PromptReply
key={uuid} key={props.uuid}
uuid={uuid} ref={isLastMessage ? replyRef : null}
reply={content} uuid={props.uuid}
pending={pending} reply={props.content}
sources={sources} pending={props.pending}
error={error} sources={props.sources}
error={props.error}
workspace={workspace} workspace={workspace}
closed={closed} closed={props.closed}
/> />
); );
} }
return ( return (
<HistoricalMessage <HistoricalMessage
key={index} key={index}
message={content} ref={isLastMessage ? replyRef : null}
role={role} message={props.content}
role={props.role}
workspace={workspace} workspace={workspace}
sources={sources} sources={props.sources}
error={error} error={props.error}
/> />
); );
} })}
)}
</div> </div>
); );
} }