gemini3hackathon-oppy / frontend /src /hooks /useOperatorSSE.js
whung99
feat: deploy Oppy with Google API integration
0d37119
import { useState, useCallback } from 'react';
const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || (import.meta.env.DEV ? 'http://localhost:8000' : '');
export default function useOperatorSSE() {
const [toolCalls, setToolCalls] = useState([]);
const [projectStatuses, setProjectStatuses] = useState({});
const [briefText, setBriefText] = useState(null);
const [isScanning, setIsScanning] = useState(false);
const [isLive, setIsLive] = useState(false);
const startScan = useCallback(() => {
setToolCalls([]);
setProjectStatuses({});
setBriefText(null);
setIsScanning(true);
setIsLive(false);
const source = new EventSource(`${BACKEND_URL}/api/run`);
source.addEventListener('mode', (e) => {
const data = JSON.parse(e.data);
setIsLive(data.live);
});
source.addEventListener('tool_call', (e) => {
const data = JSON.parse(e.data);
setToolCalls(prev => [...prev, { tool: data.tool, project: data.project, status: 'running' }]);
});
source.addEventListener('tool_result', (e) => {
const data = JSON.parse(e.data);
setToolCalls(prev => {
const updated = [...prev];
const idx = updated.findLastIndex(t => t.tool === data.tool && t.project === data.project);
if (idx >= 0) updated[idx] = { ...updated[idx], status: 'done' };
return updated;
});
});
source.addEventListener('initiative', (e) => {
const data = JSON.parse(e.data);
setProjectStatuses(prev => ({ ...prev, [data.project]: data }));
});
source.addEventListener('brief', (e) => {
const data = JSON.parse(e.data);
setBriefText(data.text);
setIsScanning(false);
source.close();
});
source.onerror = () => {
setIsScanning(false);
source.close();
};
}, []);
const sendChat = useCallback(async (message) => {
const res = await fetch(`${BACKEND_URL}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let replyText = '';
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data:') || line.startsWith('data: ')) {
try {
const data = JSON.parse(line.replace(/^data:\s*/, ''));
if (data.type === 'chat_reply') {
replyText = data.text;
}
} catch { /* skip */ }
}
}
}
return replyText;
}, []);
return { toolCalls, projectStatuses, briefText, isScanning, isLive, startScan, sendChat };
}