LibreChat/client/src/components/Chat/Messages/MessagesView.tsx
Marco Beretta d41b07c0af
♻️ refactor: Replace fontSize Recoil atom with Jotai (#10171)
* fix: reapply chat font size on load

* refactor: streamline font size handling in localStorage

* fix: update matchMedia mock to accurately reflect desktop and touchscreen capabilities

* refactor: implement Jotai for font size management and initialize on app load

- Replaced Recoil with Jotai for font size state management across components.
- Added a new `fontSize` atom to handle font size changes and persist them in localStorage.
- Implemented `initializeFontSize` function to apply saved font size on app load.
- Updated relevant components to utilize the new font size atom.

---------

Co-authored-by: ddooochii <ddooochii@gmail.com>
Co-authored-by: Danny Avila <danny@librechat.ai>
2025-10-18 05:50:34 -04:00

105 lines
3.3 KiB
TypeScript

import { useState } from 'react';
import { useAtomValue } from 'jotai';
import { useRecoilValue } from 'recoil';
import { CSSTransition } from 'react-transition-group';
import type { TMessage } from 'librechat-data-provider';
import { useScreenshot, useMessageScrolling, useLocalize } from '~/hooks';
import ScrollToBottom from '~/components/Messages/ScrollToBottom';
import { MessagesViewProvider } from '~/Providers';
import { fontSizeAtom } from '~/store/fontSize';
import MultiMessage from './MultiMessage';
import { cn } from '~/utils';
import store from '~/store';
function MessagesViewContent({
messagesTree: _messagesTree,
}: {
messagesTree?: TMessage[] | null;
}) {
const localize = useLocalize();
const fontSize = useAtomValue(fontSizeAtom);
const { screenshotTargetRef } = useScreenshot();
const scrollButtonPreference = useRecoilValue(store.showScrollButton);
const [currentEditId, setCurrentEditId] = useState<number | string | null>(-1);
const {
conversation,
scrollableRef,
messagesEndRef,
showScrollButton,
handleSmoothToRef,
debouncedHandleScroll,
} = useMessageScrolling(_messagesTree);
const { conversationId } = conversation ?? {};
return (
<>
<div className="relative flex-1 overflow-hidden overflow-y-auto">
<div className="relative h-full">
<div
className="scrollbar-gutter-stable"
onScroll={debouncedHandleScroll}
ref={scrollableRef}
style={{
height: '100%',
overflowY: 'auto',
width: '100%',
}}
>
<div className="flex flex-col pb-9 dark:bg-transparent">
{(_messagesTree && _messagesTree.length == 0) || _messagesTree === null ? (
<div
className={cn(
'flex w-full items-center justify-center p-3 text-text-secondary',
fontSize,
)}
>
{localize('com_ui_nothing_found')}
</div>
) : (
<>
<div ref={screenshotTargetRef}>
<MultiMessage
key={conversationId}
messagesTree={_messagesTree}
messageId={conversationId ?? null}
setCurrentEditId={setCurrentEditId}
currentEditId={currentEditId ?? null}
/>
</div>
</>
)}
<div
id="messages-end"
className="group h-0 w-full flex-shrink-0"
ref={messagesEndRef}
/>
</div>
</div>
<CSSTransition
in={showScrollButton && scrollButtonPreference}
timeout={{
enter: 550,
exit: 700,
}}
classNames="scroll-animation"
unmountOnExit={true}
appear={true}
>
<ScrollToBottom scrollHandler={handleSmoothToRef} />
</CSSTransition>
</div>
</div>
</>
);
}
export default function MessagesView({ messagesTree }: { messagesTree?: TMessage[] | null }) {
return (
<MessagesViewProvider>
<MessagesViewContent messagesTree={messagesTree} />
</MessagesViewProvider>
);
}