anything-llm/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx
Sean Hatfield 7b30dd041b
Add down arrow to scroll user to bottom of page (#400)
* fix sizing of onboarding modals & lint

* fix extra scrolling on mobile onboarding flow

* added message to use desktop for onboarding

* linting

* add arrow to scroll to bottom (debounced) and fix chat scrolling to always scroll to very bottom on message history change

* fix for empty chat

* change mobile alert copy

* fix div with bullet points to use list-disc instead

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>
2023-12-04 13:27:07 -08:00

137 lines
4.2 KiB
JavaScript

import HistoricalMessage from "./HistoricalMessage";
import PromptReply from "./PromptReply";
import { useEffect, useRef, useState } from "react";
import { useManageWorkspaceModal } from "../../../Modals/MangeWorkspace";
import ManageWorkspace from "../../../Modals/MangeWorkspace";
import { ArrowDown } from "@phosphor-icons/react";
import debounce from "lodash.debounce";
export default function ChatHistory({ history = [], workspace }) {
const replyRef = useRef(null);
const { showing, showModal, hideModal } = useManageWorkspaceModal();
const [isAtBottom, setIsAtBottom] = useState(true);
const chatHistoryRef = useRef(null);
useEffect(() => {
scrollToBottom();
}, [history]);
const handleScroll = () => {
const isBottom =
chatHistoryRef.current.scrollHeight - chatHistoryRef.current.scrollTop ===
chatHistoryRef.current.clientHeight;
setIsAtBottom(isBottom);
};
const debouncedScroll = debounce(handleScroll, 100);
useEffect(() => {
if(!chatHistoryRef.current) return null;
const chatHistoryElement = chatHistoryRef.current;
chatHistoryElement.addEventListener("scroll", debouncedScroll);
return () => {
chatHistoryElement.removeEventListener("scroll", debouncedScroll);
debouncedScroll.cancel();
};
}, []);
const scrollToBottom = () => {
if (chatHistoryRef.current) {
chatHistoryRef.current.scrollTo({
top: chatHistoryRef.current.scrollHeight,
behavior: "smooth",
});
}
};
if (history.length === 0) {
return (
<div className="flex flex-col h-full md:mt-0 pb-48 w-full justify-end items-center">
<div className="flex flex-col items-start">
<p className="text-white/60 text-lg font-base -ml-6 py-4">
Welcome to your new workspace.
</p>
<div className="w-full text-center">
<p className="text-white/60 text-lg font-base inline-flex items-center gap-x-2">
To get started either{" "}
<span
className="underline font-medium cursor-pointer"
onClick={showModal}
>
upload a document
</span>
or <b className="font-medium italic">send a chat.</b>
</p>
</div>
</div>
{showing && (
<ManageWorkspace
hideModal={hideModal}
providedSlug={workspace.slug}
/>
)}
</div>
);
}
return (
<div
className="h-full md:h-[83%] pb-[100px] pt-6 md:pt-0 md:pb-20 md:mx-0 overflow-y-scroll flex flex-col justify-start no-scroll"
id="chat-history"
ref={chatHistoryRef}
>
{history.map((props, index) => {
const isLastMessage = index === history.length - 1;
const isLastBotReply =
index === history.length - 1 && props.role === "assistant";
if (isLastBotReply && props.animate) {
return (
<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}
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}
/>
);
})}
{showing && (
<ManageWorkspace hideModal={hideModal} providedSlug={workspace.slug} />
)}
{!isAtBottom && (
<div className="fixed bottom-40 right-10 md:right-20 z-50 cursor-pointer animate-pulse">
<div className="flex flex-col items-center">
<div className="p-1 rounded-full border border-white/10 bg-white/10 hover:bg-white/20 hover:text-white">
<ArrowDown
weight="bold"
className="text-white/60 w-5 h-5"
onClick={scrollToBottom}
/>
</div>
</div>
</div>
)}
</div>
);
}