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,66 +1,56 @@
import { useEffect, useRef, memo } from "react";
import { memo, forwardRef } from "react";
import { AlertTriangle } from "react-feather";
import Jazzicon from "../../../../UserIcon";
import renderMarkdown from "../../../../../utils/chat/markdown";
import { userFromStorage } from "../../../../../utils/request";
import Citations from "../Citation";
function HistoricalMessage({
message,
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") {
return (
<div className="flex justify-end mb-4 items-start">
<div className="mr-2 py-1 px-4 w-fit md:max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm">
<span
className={`inline-block p-2 rounded-lg whitespace-pre-line text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base`}
>
{message}
</span>
const HistoricalMessage = forwardRef(
({ message, role, workspace, sources = [], error = false }, ref) => {
if (role === "user") {
return (
<div className="flex justify-end mb-4 items-start">
<div className="mr-2 py-1 px-4 w-fit md:max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm">
<span
className={`inline-block p-2 rounded-lg whitespace-pre-line text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base`}
>
{message}
</span>
</div>
<Jazzicon size={30} user={{ uid: userFromStorage()?.username }} />
</div>
<Jazzicon size={30} user={{ uid: userFromStorage()?.username }} />
</div>
);
}
);
}
if (error) {
return (
<div className="flex justify-start mb-4 items-end">
<Jazzicon size={30} user={{ uid: workspace.slug }} />
<div className="ml-2 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 rounded-lg bg-red-50 text-red-500`}
>
<AlertTriangle className="h-4 w-4 mb-1 inline-block" /> Could not
respond to message.
</span>
</div>
</div>
);
}
if (error) {
return (
<div className="flex justify-start mb-4 items-end">
<div ref={ref} className="flex justify-start items-end mb-4">
<Jazzicon size={30} user={{ uid: workspace.slug }} />
<div className="ml-2 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
className={`inline-block p-2 rounded-lg bg-red-50 text-red-500`}
>
<AlertTriangle className="h-4 w-4 mb-1 inline-block" /> Could not
respond to message.
</span>
className="no-scroll whitespace-pre-line text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base flex flex-col gap-y-1"
dangerouslySetInnerHTML={{ __html: renderMarkdown(message) }}
/>
<Citations sources={sources} />
</div>
</div>
);
}
return (
<div ref={replyRef} className="flex justify-start items-end mb-4">
<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">
<span
className="no-scroll whitespace-pre-line text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base flex flex-col gap-y-1"
dangerouslySetInnerHTML={{ __html: renderMarkdown(message) }}
/>
<Citations sources={sources} />
</div>
</div>
);
}
);
export default memo(HistoricalMessage);

View File

@ -1,71 +1,61 @@
import { memo, useEffect, useRef } from "react";
import { forwardRef, memo } from "react";
import { AlertTriangle } from "react-feather";
import Jazzicon from "../../../../UserIcon";
import renderMarkdown from "../../../../../utils/chat/markdown";
import Citations from "../Citation";
function PromptReply({
uuid,
reply,
pending,
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 (pending) {
return (
<div className="chat__message flex justify-start mb-4 items-end">
<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">
<span className={`inline-block p-2`}>
<div className="dot-falling"></div>
</span>
</div>
</div>
);
}
if (error) {
return (
<div className="chat__message flex justify-start mb-4 items-center">
<Jazzicon size={30} user={{ uid: workspace.slug }} />
<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">
<span className={`inline-block`}>
<AlertTriangle className="h-4 w-4 mb-1 inline-block" /> Could not
respond to message.
const PromptReply = forwardRef(
(
{ uuid, reply, pending, error, workspace, sources = [], closed = true },
ref
) => {
if (!reply && !sources.length === 0 && !pending && !error) return null;
if (pending) {
return (
<div
ref={ref}
className="chat__message flex justify-start mb-4 items-end"
>
<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">
<span className={`inline-block p-2`}>
<div className="dot-falling"></div>
</span>
<span className="text-xs">Reason: {error || "unknown"}</span>
</div>
</div>
);
}
if (error) {
return (
<div className="chat__message flex justify-start mb-4 items-center">
<Jazzicon size={30} user={{ uid: workspace.slug }} />
<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">
<span className={`inline-block`}>
<AlertTriangle className="h-4 w-4 mb-1 inline-block" /> Could
not respond to message.
</span>
<span className="text-xs">Reason: {error || "unknown"}</span>
</div>
</div>
</div>
);
}
return (
<div key={uuid} ref={ref} className="mb-4 flex justify-start items-end">
<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">
<span
className="whitespace-pre-line text-slate-800 dark:text-slate-200 flex flex-col gap-y-1 font-[500] md:font-semibold text-sm md:text-base"
dangerouslySetInnerHTML={{ __html: renderMarkdown(reply) }}
/>
<Citations sources={sources} />
</div>
</div>
);
}
return (
<div
key={uuid}
ref={replyRef}
className="mb-4 flex justify-start items-end"
>
<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">
<span
className="whitespace-pre-line text-slate-800 dark:text-slate-200 flex flex-col gap-y-1 font-[500] md:font-semibold text-sm md:text-base"
dangerouslySetInnerHTML={{ __html: renderMarkdown(reply) }}
/>
<Citations sources={sources} />
</div>
</div>
);
}
);
export default memo(PromptReply);

View File

@ -1,8 +1,19 @@
import { Frown } from "react-feather";
import HistoricalMessage from "./HistoricalMessage";
import PromptReply from "./PromptReply";
import { useEffect, useRef } from "react";
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) {
return (
<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"
id="chat-history"
>
{history.map(
(
{
uuid = null,
content,
sources = [],
role,
closed = true,
pending = false,
error = false,
animate = false,
},
index
) => {
const isLastBotReply =
index === history.length - 1 && role === "assistant";
if (isLastBotReply && animate) {
return (
<PromptReply
key={uuid}
uuid={uuid}
reply={content}
pending={pending}
sources={sources}
error={error}
workspace={workspace}
closed={closed}
/>
);
}
{history.map((props, index) => {
const isLastMessage = index === history.length - 1;
if (props.role === "assistant" && props.animate) {
return (
<HistoricalMessage
key={index}
message={content}
role={role}
<PromptReply
key={props.uuid}
ref={isLastMessage ? replyRef : null}
uuid={props.uuid}
reply={props.content}
pending={props.pending}
sources={props.sources}
error={props.error}
workspace={workspace}
sources={sources}
error={error}
closed={props.closed}
/>
);
}
)}
return (
<HistoricalMessage
key={index}
ref={isLastMessage ? replyRef : null}
message={props.content}
role={props.role}
workspace={workspace}
sources={props.sources}
error={props.error}
/>
);
})}
</div>
);
}