unify card/modal

This commit is contained in:
teernisse
2026-02-26 10:20:26 -05:00
parent 31862f3a40
commit fa1ad4b22b
7 changed files with 522 additions and 238 deletions

View File

@@ -10,7 +10,6 @@ export function App() {
const [sessions, setSessions] = useState([]);
const [modalSession, setModalSession] = useState(null);
const [conversations, setConversations] = useState({});
const [conversationLoading, setConversationLoading] = useState(false);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [selectedProject, setSelectedProject] = useState(null);
@@ -40,6 +39,12 @@ export function App() {
// Track last_event_at for each session to detect actual changes
const lastEventAtRef = useRef({});
// Refs for stable callback access (avoids recreation on state changes)
const sessionsRef = useRef(sessions);
const conversationsRef = useRef(conversations);
sessionsRef.current = sessions;
conversationsRef.current = conversations;
// Apply state payload from polling or SSE stream
const applyStateData = useCallback((data) => {
const newSessions = data.sessions || [];
@@ -98,13 +103,9 @@ export function App() {
}, [applyStateData]);
// Fetch conversation for a session
const fetchConversation = useCallback(async (sessionId, projectDir, agent = 'claude', showLoading = false, force = false) => {
const fetchConversation = useCallback(async (sessionId, projectDir, agent = 'claude', force = false) => {
// Skip if already fetched and not forcing refresh
if (!force && conversations[sessionId]) return;
if (showLoading) {
setConversationLoading(true);
}
if (!force && conversationsRef.current[sessionId]) return;
try {
let url = API_CONVERSATION + encodeURIComponent(sessionId);
@@ -124,12 +125,8 @@ export function App() {
}));
} catch (err) {
console.error('Error fetching conversation:', err);
} finally {
if (showLoading) {
setConversationLoading(false);
}
}
}, [conversations]);
}, []);
// Respond to a session's pending question
const respondToSession = useCallback(async (sessionId, text, isFreeform = false, optionCount = 0) => {
@@ -146,13 +143,19 @@ export function App() {
});
const data = await res.json();
if (data.ok) {
// Trigger refresh
// Trigger state refresh
fetchState();
// Refresh conversation for immediate feedback
const session = sessionsRef.current.find(s => s.session_id === sessionId);
if (session) {
await refreshConversationSilent(sessionId, session.project_dir, session.agent || 'claude');
}
}
} catch (err) {
console.error('Error responding to session:', err);
throw err; // Re-throw so SimpleInput/QuestionBlock can catch and show error
}
}, [fetchState]);
}, [fetchState, refreshConversationSilent]);
// Dismiss a session
const dismissSession = useCallback(async (sessionId) => {
@@ -258,20 +261,9 @@ export function App() {
setModalSession(session);
// Fetch conversation if not already cached
if (!conversations[session.session_id]) {
await fetchConversation(session.session_id, session.project_dir, session.agent || 'claude', true);
if (!conversationsRef.current[session.session_id]) {
await fetchConversation(session.session_id, session.project_dir, session.agent || 'claude');
}
}, [conversations, fetchConversation]);
// Refresh conversation (force re-fetch, used after sending messages)
const refreshConversation = useCallback(async (sessionId, projectDir, agent = 'claude') => {
// Force refresh by clearing cache first
setConversations(prev => {
const updated = { ...prev };
delete updated[sessionId];
return updated;
});
await fetchConversation(sessionId, projectDir, agent, false, true);
}, [fetchConversation]);
const handleCloseModal = useCallback(() => {
@@ -383,10 +375,10 @@ export function App() {
<${Modal}
session=${modalSession}
conversations=${conversations}
conversationLoading=${conversationLoading}
onClose=${handleCloseModal}
onSendMessage=${respondToSession}
onRefreshConversation=${refreshConversation}
onFetchConversation=${fetchConversation}
onRespond=${respondToSession}
onDismiss=${dismissSession}
/>
`;
}