- Fix Goose assistant response spam: suppress role='assistant' notifications - Fix stale tab state: use fresh shouldSuppressNotification calls - Remove buggy legacy /pair suppression logic that checked sender ID - Fix 'Open Chat' to check existing tabs before navigating - Add Matrix room ID display in SessionListView - Optimize MatrixSessionService to defer heavy loading until needed - Add loading state for Matrix chat initialization - Improve loading indicator visibility in BaseChat2 Fixes notification spam, incorrect suppression, and blank chat states.
5.7 KiB
Loading State for Matrix Chat Initialization
Summary
Added a prominent loading state when opening Matrix chats from notifications to provide visual feedback while the conversation is being initialized and message history is being loaded.
Problem
When clicking "Open Chat" on a Matrix notification, the chat would appear empty for a few seconds while:
- Backend session was being created or retrieved
- Matrix room history was being loaded
- Session mapping was being established
This made it look like the chat was broken or empty, even though it was just loading.
Solution
Implemented a two-phase loading state:
Phase 1: Create Temporary Tab with Loading State
When openMatrixChat is called, immediately create a tab with:
loadingChat: true- Shows loading indicator- Temporary session ID (
temp_matrix_${timestamp}) - Tab is immediately visible to user
Phase 2: Update with Real Session
Asynchronously:
- Get or create backend session mapping
- Update tab with real backend session ID
- Set
loadingChat: false- Removes loading indicator - Message history loads via
useChatStream
Changes Made
1. TabContext.tsx - Add Loading State to openMatrixChat
File: ui/desktop/src/contexts/TabContext.tsx
Change: Modified openMatrixChat to create a temporary loading tab immediately, then update it with the real session ID asynchronously.
Before:
const openMatrixChat = useCallback(async (roomId: string, senderId: string) => {
// Check for existing tab...
// Get or create backend session (blocking)
let backendSessionId = sessionMappingService.getGooseSessionId(roomId);
// ... create session if needed ...
// Create tab with real session ID
const newTab = createNewTab({ sessionId: backendSessionId, ... });
const newTabState = { tab: newTab, chat: {...}, loadingChat: false };
setTabStates(prev => [...prev, newTabState]);
setActiveTabId(newTab.id);
}, [tabStates]);
After:
const openMatrixChat = useCallback(async (roomId: string, senderId: string) => {
// Check for existing tab...
// Create temporary tab with loading state IMMEDIATELY
const tempTab = createNewTab({
sessionId: `temp_matrix_${Date.now()}`,
title: `Chat with ${senderName}`,
type: 'matrix',
matrixRoomId: roomId,
matrixRecipientId: senderId,
isActive: true
});
const tempTabState = {
tab: tempTab,
chat: {...},
loadingChat: true // Show loading indicator
};
// Add loading tab immediately for instant feedback
setTabStates(prev => [...prev, tempTabState]);
setActiveTabId(tempTab.id);
// Get or create backend session (async, doesn't block UI)
let backendSessionId = sessionMappingService.getGooseSessionId(roomId);
// ... create session if needed ...
// Update tab with real session ID and remove loading state
setTabStates(prev => prev.map(ts =>
ts.tab.id === tempTab.id
? {
...ts,
tab: { ...ts.tab, sessionId: backendSessionId },
chat: { ...ts.chat, sessionId: backendSessionId },
loadingChat: false // Remove loading indicator
}
: ts
));
}, [tabStates]);
2. BaseChat2.tsx - Improve Loading Indicator Visibility
File: ui/desktop/src/components/BaseChat2.tsx
Change: Made the loading indicator more prominent and centered.
Before:
{loadingChat && (
<div className="px-6 py-4">
<LoadingGoose
message="loading conversation..."
chatState={ChatState.Idle}
/>
</div>
)}
After:
{loadingChat && (
<div className="flex items-center justify-center h-full min-h-[400px]">
<div className="text-center">
<LoadingGoose
message="Loading conversation..."
chatState={ChatState.Idle}
/>
<p className="text-text-muted text-sm mt-4">
Fetching message history...
</p>
</div>
</div>
)}
User Experience Flow
Before:
- User clicks "Open Chat" on notification
- [2-3 second delay with empty chat]
- Messages suddenly appear
After:
- User clicks "Open Chat" on notification
- Tab opens immediately with loading indicator
- Loading message: "Loading conversation... Fetching message history..."
- Messages load and loading indicator disappears
Benefits
- Instant Feedback: Tab opens immediately, no perceived delay
- Clear Communication: User knows the app is working, not frozen
- Better UX: Loading state prevents confusion about empty chats
- Non-Blocking: Backend session creation doesn't block UI rendering
- Smooth Transition: Loading indicator smoothly transitions to loaded messages
Testing
-
Test New Matrix Chat:
- Click "Open Chat" on a notification for a room you haven't opened yet
- ✅ Tab should open immediately with loading indicator
- ✅ Loading message should be visible and centered
- ✅ After 1-2 seconds, messages should load and loading indicator disappears
-
Test Existing Matrix Chat:
- Click "Open Chat" on a notification for a room already open in a tab
- ✅ Should switch to existing tab immediately (no loading state)
-
Test Slow Network:
- Simulate slow network conditions
- ✅ Loading indicator should remain visible until session is ready
- ✅ User should never see an empty chat without explanation
Technical Details
- Loading State Prop:
loadingChat: booleaninTabState - Temporary Session ID:
temp_matrix_${timestamp}format - Session Update: Uses
setTabStatesto update specific tab without re-creating - Loading Component:
LoadingGoosewith custom message - Minimum Height:
min-h-[400px]ensures loading indicator is visible even in small windows