projects/deliberation_at_scale/packages/frontend/components/RoomChatSummary.tsx (118 lines of code) (raw):

'use client'; import { AnimatePresence, motion } from 'framer-motion'; import { isEmpty } from 'radash'; import RoomTopic from './RoomTopic'; import Pill from "./Pill"; import useRoom from "@/hooks/useRoom"; import { useSendRoomMessageMutation } from '@/generated/graphql'; import ChatInput from './ChatInput'; import RoomOutcome from './RoomOutcome'; import ReactMarkdown from 'react-markdown'; import { MESSAGES_SCROLL_CONTAINER_ID } from '@/utilities/constants'; import { useEffect } from 'react'; import { useAppDispatch } from '@/state/store'; import { openRoomAssistant } from '@/state/slices/room'; import useRoomActions from '@/hooks/useRoomActions'; import ChatActions from './ChatActions'; import Divider from './Divider'; import { usePingParticipant } from '@/hooks/usePingParticipant'; const ENABLE_CHAT = false; const messageAnimation = { initial: { opacity: 0, x: 300 }, animate: { opacity: 1, x: 0, transition: { duration: 0.3 } }, exit: { opacity: 0, x: -100, transition: { duration: 0.15 } }, }; export default function RoomChatSummary() { const { lastBotMessages, lastParticipantMessages, participantId, participant, participants, roomId, outcomes } = useRoom(); const { actions } = useRoomActions(); const [sendRoomMessage] = useSendRoomMessageMutation(); const dispatch = useAppDispatch(); // keep track of when this component was shown for the last time useEffect(() => { dispatch(openRoomAssistant()); }, [dispatch]); // ping the participant entry to know which participants are still in the room usePingParticipant(participant); return ( <div className="flex flex-col gap-2 pb-4 justify-between min-h-0"> <RoomTopic /> <motion.div id={MESSAGES_SCROLL_CONTAINER_ID} className="flex flex-col gap-4 overflow-y-scroll" initial={{ opacity: 0, y: 40 }} animate={{ opacity: 1, y: 0 }} > <AnimatePresence> {outcomes?.map((outcome) => { const { id: outcomeId } = outcome; return ( <RoomOutcome key={outcomeId} outcome={outcome} participantId={participantId} participants={participants} /> ); })} </AnimatePresence> <div className="flex flex-col gap-2"> <AnimatePresence mode="wait" initial={false}> {lastBotMessages.map((message) => { const { id, content, name, nameIcon } = message; const formattedContent = content?.trim(); return ( <motion.div key={id} layoutId={id} {...messageAnimation} > <Pill icon={nameIcon} className="mb-4">{name}</Pill> <ReactMarkdown>{formattedContent}</ReactMarkdown> </motion.div> ); })} </AnimatePresence> </div> {!isEmpty(lastParticipantMessages) && !isEmpty(lastBotMessages) && ( <Divider /> )} <div className="flex flex-col gap-2"> <AnimatePresence mode="wait" initial={false}> {lastParticipantMessages.map((message) => { const { id, content, name } = message; const formattedContent = content?.trim(); return ( <motion.div key={id} layoutId={id} {...messageAnimation} > <strong>{name}</strong> <ReactMarkdown>{formattedContent}</ReactMarkdown> </motion.div> ); })} </AnimatePresence> </div> </motion.div> <div className="flex flex-col gap-4"> {!isEmpty(actions) && ( <Divider /> )} <ChatActions actions={actions} /> {ENABLE_CHAT && ( <ChatInput onSubmit={async (input) => { sendRoomMessage({ variables: { content: input.content, roomId, participantId, } }); return true; }} /> )} </div> </div> ); }