mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-07-01 10:02:57 +08:00
fix(chat): refine load all overlay behavior
This commit is contained in:
@@ -338,12 +338,36 @@ export function useChatSessionState({
|
|||||||
const slot = await sessionStore.fetchMore(selectedSession.id, {
|
const slot = await sessionStore.fetchMore(selectedSession.id, {
|
||||||
limit: MESSAGES_PER_PAGE,
|
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 };
|
pendingScrollRestoreRef.current = { height: previousScrollHeight, top: previousScrollTop };
|
||||||
setHasMoreMessages(slot.hasMore);
|
setHasMoreMessages(slot.hasMore);
|
||||||
setTotalMessages(slot.total);
|
setTotalMessages(slot.total);
|
||||||
setVisibleMessageCount((prev) => prev + MESSAGES_PER_PAGE);
|
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;
|
return true;
|
||||||
} finally {
|
} finally {
|
||||||
isLoadingMoreRef.current = false;
|
isLoadingMoreRef.current = false;
|
||||||
@@ -371,7 +395,7 @@ export function useChatSessionState({
|
|||||||
loadAllOverlayTimerRef.current = setTimeout(() => {
|
loadAllOverlayTimerRef.current = setTimeout(() => {
|
||||||
setShowLoadAllOverlay(false);
|
setShowLoadAllOverlay(false);
|
||||||
loadAllOverlayTimerRef.current = null;
|
loadAllOverlayTimerRef.current = null;
|
||||||
}, 5000);
|
}, 2500);
|
||||||
}
|
}
|
||||||
} else if (!scrolledNearTop) {
|
} else if (!scrolledNearTop) {
|
||||||
wasNearTopRef.current = false;
|
wasNearTopRef.current = false;
|
||||||
@@ -786,7 +810,7 @@ export function useChatSessionState({
|
|||||||
setLoadAllJustFinished(false);
|
setLoadAllJustFinished(false);
|
||||||
setShowLoadAllOverlay(false);
|
setShowLoadAllOverlay(false);
|
||||||
loadAllFinishedTimerRef.current = null;
|
loadAllFinishedTimerRef.current = null;
|
||||||
}, 1000);
|
}, 2500);
|
||||||
} else {
|
} else {
|
||||||
allMessagesLoadedRef.current = false;
|
allMessagesLoadedRef.current = false;
|
||||||
setShowLoadAllOverlay(false);
|
setShowLoadAllOverlay(false);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { groupConsecutiveTools, isToolGroupItem } from '../../utils/toolGrouping
|
|||||||
import MessageComponent from './MessageComponent';
|
import MessageComponent from './MessageComponent';
|
||||||
import ProviderSelectionEmptyState from './ProviderSelectionEmptyState';
|
import ProviderSelectionEmptyState from './ProviderSelectionEmptyState';
|
||||||
import ToolGroupContainer from './ToolGroupContainer';
|
import ToolGroupContainer from './ToolGroupContainer';
|
||||||
|
import LoadAllMessagesOverlay from './LoadAllMessagesOverlay';
|
||||||
|
|
||||||
interface ChatMessagesPaneProps {
|
interface ChatMessagesPaneProps {
|
||||||
scrollContainerRef: RefObject<HTMLDivElement>;
|
scrollContainerRef: RefObject<HTMLDivElement>;
|
||||||
@@ -219,35 +220,13 @@ function ChatMessagesPane({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Floating "Load all messages" overlay */}
|
<LoadAllMessagesOverlay
|
||||||
{(showLoadAllOverlay || isLoadingAllMessages || loadAllJustFinished) && (
|
showLoadAllOverlay={showLoadAllOverlay}
|
||||||
<div className="pointer-events-none sticky top-2 z-20 flex justify-center">
|
isLoadingAllMessages={isLoadingAllMessages}
|
||||||
{loadAllJustFinished ? (
|
loadAllJustFinished={loadAllJustFinished}
|
||||||
<div className="flex items-center space-x-2 rounded-full bg-green-600 px-4 py-1.5 text-xs font-medium text-white shadow-lg dark:bg-green-500">
|
totalMessages={totalMessages}
|
||||||
<svg className="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
onLoadAllMessages={loadAllMessages}
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
/>
|
||||||
</svg>
|
|
||||||
<span>{t('session.messages.allLoaded')}</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
className="pointer-events-auto flex items-center space-x-2 rounded-full bg-blue-600 px-4 py-1.5 text-xs font-medium text-white shadow-lg transition-all duration-200 hover:scale-105 hover:bg-blue-700 disabled:cursor-wait disabled:opacity-75 dark:bg-blue-500 dark:hover:bg-blue-600"
|
|
||||||
onClick={loadAllMessages}
|
|
||||||
disabled={isLoadingAllMessages}
|
|
||||||
>
|
|
||||||
{isLoadingAllMessages && (
|
|
||||||
<div className="h-3 w-3 animate-spin rounded-full border-2 border-white/30 border-t-white" />
|
|
||||||
)}
|
|
||||||
<span>
|
|
||||||
{isLoadingAllMessages
|
|
||||||
? t('session.messages.loadingAll')
|
|
||||||
: <>{t('session.messages.loadAll')} {totalMessages > 0 && `(${totalMessages})`}</>
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Legacy message count indicator (for non-paginated view) */}
|
{/* Legacy message count indicator (for non-paginated view) */}
|
||||||
{!hasMoreMessages && chatMessages.length > visibleMessageCount && (
|
{!hasMoreMessages && chatMessages.length > visibleMessageCount && (
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
<div
|
||||||
|
className={`pointer-events-none sticky top-2 z-20 flex justify-center ${!isLoadingAllMessages ? 'load-all-overlay-auto-fade' : ''}`}
|
||||||
|
style={!isLoadingAllMessages ? { animation: 'loadAllOverlayAutoFade 2500ms ease forwards' } : undefined}
|
||||||
|
>
|
||||||
|
<style>{loadAllOverlayAnimationStyle}</style>
|
||||||
|
{loadAllJustFinished ? (
|
||||||
|
<div className="flex items-center space-x-2 rounded-full bg-green-600 px-4 py-1.5 text-xs font-medium text-white shadow-lg dark:bg-green-500">
|
||||||
|
<svg className="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
<span>{t('session.messages.allLoaded')}</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
className="pointer-events-auto flex items-center space-x-2 rounded-full bg-blue-600 px-4 py-1.5 text-xs font-medium text-white shadow-lg transition-all duration-200 hover:scale-105 hover:bg-blue-700 disabled:cursor-wait disabled:opacity-75 dark:bg-blue-500 dark:hover:bg-blue-600"
|
||||||
|
onClick={onLoadAllMessages}
|
||||||
|
disabled={isLoadingAllMessages}
|
||||||
|
>
|
||||||
|
{isLoadingAllMessages && (
|
||||||
|
<div className="h-3 w-3 animate-spin rounded-full border-2 border-white/30 border-t-white" />
|
||||||
|
)}
|
||||||
|
<span>
|
||||||
|
{isLoadingAllMessages
|
||||||
|
? t('session.messages.loadingAll')
|
||||||
|
: <>{t('session.messages.loadAll')} {totalMessages > 0 && `(${totalMessages})`}</>}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user