From cb882f5eadaba551a6072e8f612fe57971ac42da Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Thu, 12 Feb 2026 05:52:06 +0000 Subject: [PATCH] feat: add loading indicator when switching sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show a spinner with 'Loading messages…' text while chat history is being fetched during session switches, instead of briefly flashing the empty welcome screen. Includes EN/FR i18n. --- src/App.tsx | 4 ++-- src/components/Chat.tsx | 13 ++++++++++--- src/hooks/useGateway.ts | 6 +++++- src/lib/i18n.ts | 2 ++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a62f749..9f69d76 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,7 +10,7 @@ import { KeyboardShortcuts } from './components/KeyboardShortcuts'; export default function App() { const { - status, messages, sessions, activeSession, isGenerating, + status, messages, sessions, activeSession, isGenerating, isLoadingHistory, sendMessage, abort, switchSession, authenticated, login, logout, connectError, isConnecting, } = useGateway(); @@ -77,7 +77,7 @@ export default function App() {
setSidebarOpen(!sidebarOpen)} activeSessionData={sessions.find(s => s.key === activeSession)} onLogout={logout} /> - +
setShortcutsOpen(false)} /> diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index caf58b9..dbd6aa5 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -3,13 +3,14 @@ import { ChatMessageComponent } from './ChatMessage'; import { ChatInput } from './ChatInput'; import { TypingIndicator } from './TypingIndicator'; import type { ChatMessage, ConnectionStatus } from '../types'; -import { Bot, ArrowDown } from 'lucide-react'; +import { Bot, ArrowDown, Loader2 } from 'lucide-react'; import { useT } from '../hooks/useLocale'; import { getLocale, type TranslationKey } from '../lib/i18n'; interface Props { messages: ChatMessage[]; isGenerating: boolean; + isLoadingHistory: boolean; status: ConnectionStatus; onSend: (text: string, attachments?: Array<{ mimeType: string; fileName: string; content: string }>) => void; onAbort: () => void; @@ -63,7 +64,7 @@ function getDateKey(ts: number): string { /** Threshold in pixels — if the user is within this distance of the bottom, auto-scroll */ const SCROLL_THRESHOLD = 150; -export function Chat({ messages, isGenerating, status, onSend, onAbort }: Props) { +export function Chat({ messages, isGenerating, isLoadingHistory, status, onSend, onAbort }: Props) { const t = useT(); const bottomRef = useRef(null); const scrollContainerRef = useRef(null); @@ -128,7 +129,13 @@ export function Chat({ messages, isGenerating, status, onSend, onAbort }: Props)
- {messages.length === 0 && ( + {messages.length === 0 && isLoadingHistory && ( +
+ +
{t('chat.loadingHistory')}
+
+ )} + {messages.length === 0 && !isLoadingHistory && (
diff --git a/src/hooks/useGateway.ts b/src/hooks/useGateway.ts index ecdd3ff..8a064dc 100644 --- a/src/hooks/useGateway.ts +++ b/src/hooks/useGateway.ts @@ -28,6 +28,7 @@ export function useGateway() { const [sessions, setSessions] = useState([]); const [activeSession, setActiveSession] = useState('agent:main:main'); const [isGenerating, setIsGenerating] = useState(false); + const [isLoadingHistory, setIsLoadingHistory] = useState(false); const [authenticated, setAuthenticated] = useState(null); // null = checking const [connectError, setConnectError] = useState(null); const [isConnecting, setIsConnecting] = useState(false); @@ -100,6 +101,7 @@ export function useGateway() { }, []); const loadHistory = useCallback(async (sessionKey: string) => { + setIsLoadingHistory(true); try { const res = await clientRef.current?.send('chat.history', { sessionKey, limit: 100 }); const rawMsgs = res?.messages as Array> | undefined; @@ -173,6 +175,8 @@ export function useGateway() { } } catch { // Silently ignore history load failures + } finally { + setIsLoadingHistory(false); } }, []); @@ -388,7 +392,7 @@ export function useGateway() { })); return { - status, messages, sessions: enrichedSessions, activeSession, isGenerating, + status, messages, sessions: enrichedSessions, activeSession, isGenerating, isLoadingHistory, sendMessage, abort, switchSession, loadSessions, authenticated, login, logout, connectError, isConnecting, }; diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts index 8652589..9c7bc80 100644 --- a/src/lib/i18n.ts +++ b/src/lib/i18n.ts @@ -31,6 +31,7 @@ const en = { // Chat 'chat.welcome': 'PinchChat', 'chat.welcomeSub': 'Send a message to get started', + 'chat.loadingHistory': 'Loading messages…', 'chat.inputPlaceholder': 'Type a message…', 'chat.inputLabel': 'Message', 'chat.attachFile': 'Attach file', @@ -106,6 +107,7 @@ const fr: Record = { 'chat.welcome': 'PinchChat', 'chat.welcomeSub': 'Envoyez un message pour commencer', + 'chat.loadingHistory': 'Chargement des messages…', 'chat.inputPlaceholder': 'Tapez un message…', 'chat.inputLabel': 'Message', 'chat.attachFile': 'Joindre un fichier',