diff --git a/src/components/chat/hooks/useChatSessionState.ts b/src/components/chat/hooks/useChatSessionState.ts index 47f66830..3330e21b 100644 --- a/src/components/chat/hooks/useChatSessionState.ts +++ b/src/components/chat/hooks/useChatSessionState.ts @@ -338,12 +338,36 @@ export function useChatSessionState({ const slot = await sessionStore.fetchMore(selectedSession.id, { limit: MESSAGES_PER_PAGE, }); - if (!slot || slot.serverMessages.length === 0) return false; + if (!slot) return false; + if (slot.serverMessages.length === 0) { + if (!slot.hasMore) { + setHasMoreMessages(false); + allMessagesLoadedRef.current = true; + setAllMessagesLoaded(true); + if (!loadAllOverlayTimerRef.current) { + loadAllOverlayTimerRef.current = setTimeout(() => { + setShowLoadAllOverlay(false); + loadAllOverlayTimerRef.current = null; + }, 2500); + } + } + return false; + } pendingScrollRestoreRef.current = { height: previousScrollHeight, top: previousScrollTop }; setHasMoreMessages(slot.hasMore); setTotalMessages(slot.total); setVisibleMessageCount((prev) => prev + MESSAGES_PER_PAGE); + if (!slot.hasMore) { + allMessagesLoadedRef.current = true; + setAllMessagesLoaded(true); + if (!loadAllOverlayTimerRef.current) { + loadAllOverlayTimerRef.current = setTimeout(() => { + setShowLoadAllOverlay(false); + loadAllOverlayTimerRef.current = null; + }, 2500); + } + } return true; } finally { isLoadingMoreRef.current = false; @@ -371,7 +395,7 @@ export function useChatSessionState({ loadAllOverlayTimerRef.current = setTimeout(() => { setShowLoadAllOverlay(false); loadAllOverlayTimerRef.current = null; - }, 5000); + }, 2500); } } else if (!scrolledNearTop) { wasNearTopRef.current = false; @@ -786,7 +810,7 @@ export function useChatSessionState({ setLoadAllJustFinished(false); setShowLoadAllOverlay(false); loadAllFinishedTimerRef.current = null; - }, 1000); + }, 2500); } else { allMessagesLoadedRef.current = false; setShowLoadAllOverlay(false); diff --git a/src/components/chat/view/subcomponents/ChatMessagesPane.tsx b/src/components/chat/view/subcomponents/ChatMessagesPane.tsx index 78d212b9..55be2a09 100644 --- a/src/components/chat/view/subcomponents/ChatMessagesPane.tsx +++ b/src/components/chat/view/subcomponents/ChatMessagesPane.tsx @@ -15,6 +15,7 @@ import { groupConsecutiveTools, isToolGroupItem } from '../../utils/toolGrouping import MessageComponent from './MessageComponent'; import ProviderSelectionEmptyState from './ProviderSelectionEmptyState'; import ToolGroupContainer from './ToolGroupContainer'; +import LoadAllMessagesOverlay from './LoadAllMessagesOverlay'; interface ChatMessagesPaneProps { scrollContainerRef: RefObject; @@ -219,35 +220,13 @@ function ChatMessagesPane({ )} - {/* Floating "Load all messages" overlay */} - {(showLoadAllOverlay || isLoadingAllMessages || loadAllJustFinished) && ( -
- {loadAllJustFinished ? ( -
- - - - {t('session.messages.allLoaded')} -
- ) : ( - - )} -
- )} + {/* Legacy message count indicator (for non-paginated view) */} {!hasMoreMessages && chatMessages.length > visibleMessageCount && ( diff --git a/src/components/chat/view/subcomponents/LoadAllMessagesOverlay.tsx b/src/components/chat/view/subcomponents/LoadAllMessagesOverlay.tsx new file mode 100644 index 00000000..ef246756 --- /dev/null +++ b/src/components/chat/view/subcomponents/LoadAllMessagesOverlay.tsx @@ -0,0 +1,68 @@ +import { useTranslation } from 'react-i18next'; + +const loadAllOverlayAnimationStyle = ` +@keyframes loadAllOverlayAutoFade { + 0%, 80% { opacity: 1; } + 100% { opacity: 0; } +} + +@media (prefers-reduced-motion: reduce) { + .load-all-overlay-auto-fade { + animation: none !important; + } +} +`; + +interface LoadAllMessagesOverlayProps { + showLoadAllOverlay: boolean; + isLoadingAllMessages: boolean; + loadAllJustFinished: boolean; + totalMessages: number; + onLoadAllMessages: () => void; +} + +export default function LoadAllMessagesOverlay({ + showLoadAllOverlay, + isLoadingAllMessages, + loadAllJustFinished, + totalMessages, + onLoadAllMessages, +}: LoadAllMessagesOverlayProps) { + const { t } = useTranslation('chat'); + + if (!showLoadAllOverlay && !isLoadingAllMessages && !loadAllJustFinished) { + return null; + } + + return ( +
+ + {loadAllJustFinished ? ( +
+ + + + {t('session.messages.allLoaded')} +
+ ) : ( + + )} +
+ ); +}