import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Moon, Settings, SquarePen, Sun, UserPlus } from 'lucide-react'; import { WPAssistantLogo } from '../Logos/WPAssistantLogo'; import ConversationGroup from './ConversationGroup'; import { MenuItem } from '../ui'; import { cn } from '../../utils/classNames'; import { useTheme } from '../../contexts/ThemeContext'; import useGroupedConversations from '../../hooks/useGroupedConversations'; import { useAuth } from '../../contexts/AuthContext'; import { useCurrentConversationId, useConversations, useConversationsLoading, useHasMoreConversations, useIsFetchingMoreConversations, useLoadMoreConversations, } from '../../contexts/ChatContext'; import ConversationsSkeleton from './ConversationsSkeleton'; import { useSetConfirmDeleteConversationId, useSetConfirmDeleteConversationName, useSetIsConfirmDeleteModalOpen, useSetIsLoginModalOpen, useSetIsSettingsModalOpen, } from '../../contexts/UIContext'; import useRenderTracker from '../../hooks/useRenderTracker'; import QuickLinks from './QuickLinks'; import { IS_WORDPRESS } from '../../constants'; import { useAppRoute } from '../../hooks/useAppRoute'; interface SidebarContentProps { onActionComplete?: () => void; } const SidebarContent: React.FC = ({ onActionComplete }) => { const setIsLoginModalOpen = useSetIsLoginModalOpen(); const setIsConfirmDeleteModalOpen = useSetIsConfirmDeleteModalOpen(); const setConfirmDeleteConversationId = useSetConfirmDeleteConversationId(); const setIsSettingsModalOpen = useSetIsSettingsModalOpen(); const setConfirmDeleteConversationName = useSetConfirmDeleteConversationName(); const { effectiveTheme, setTheme } = useTheme(); const { isLoggedIn } = useAuth(); const currentConversationId = useCurrentConversationId(); const conversations = useConversations(); const conversationsLoading = useConversationsLoading(); const hasMoreConversations = useHasMoreConversations(); const isFetchingMoreConversations = useIsFetchingMoreConversations(); const loadMoreConversations = useLoadMoreConversations(); const groupedConversations = useGroupedConversations({ conversations }); // Show a subtle top fade only once the conversation list is scrolled down. const navRef = useRef(null); const loadMoreRef = useRef(null); const [scrolled, setScrolled] = useState(false); useEffect(() => { const el = navRef.current; if (!el) return; const onScroll = () => setScrolled(el.scrollTop > 4); onScroll(); el.addEventListener('scroll', onScroll, { passive: true }); return () => el.removeEventListener('scroll', onScroll); }, [conversationsLoading, groupedConversations]); useEffect(() => { const root = navRef.current; const target = loadMoreRef.current; if (!root || !target || !hasMoreConversations || isFetchingMoreConversations) return; const observer = new IntersectionObserver( ([entry]) => { if (entry?.isIntersecting) { void loadMoreConversations(); } }, { root, rootMargin: '160px 0px', }, ); observer.observe(target); return () => observer.disconnect(); }, [ conversations.length, hasMoreConversations, isFetchingMoreConversations, loadMoreConversations, ]); const { openConversationRoute, openHomeRoute, openSettingsRoute } = useAppRoute(); useRenderTracker('SidebarContent', { effectiveTheme, isLoggedIn, conversations, groupedConversations, currentConversationId, }); const completeAction = useCallback(() => { onActionComplete?.(); }, [onActionComplete]); const handleNewChat = useCallback(() => { openHomeRoute(); completeAction(); }, [completeAction, openHomeRoute]); const handleSelectConversation = useCallback( (conversationId: string) => { openConversationRoute(conversationId); completeAction(); }, [completeAction, openConversationRoute], ); const onDeleteConversation = useCallback( (conversationName: string, conversationId: string) => { setConfirmDeleteConversationName(conversationName); setConfirmDeleteConversationId(conversationId); setIsConfirmDeleteModalOpen(true); completeAction(); }, [ completeAction, setConfirmDeleteConversationName, setConfirmDeleteConversationId, setIsConfirmDeleteModalOpen, ], ); return (
{/* Full-width scroll area so the scrollbar sits flush to the sidebar edge; rows pad themselves. A bottom fade hints at more content below. */}
{/* Subtle top fade, just below the sticky group header, only when scrolled. */}
{!isLoggedIn && ( } onClick={() => { setIsLoginModalOpen(true); completeAction(); }} > Login / Sign Up )} {!isLoggedIn && ( : } onClick={() => setTheme(effectiveTheme === 'dark' ? 'light' : 'dark')} > {effectiveTheme === 'dark' ? 'Light Mode' : 'Dark Mode'} )} {(isLoggedIn || IS_WORDPRESS) && ( } onClick={() => { setIsSettingsModalOpen(true); openSettingsRoute(isLoggedIn ? 'account' : 'appearance'); completeAction(); }} > Settings )}
); }; export default React.memo(SidebarContent);