fix(projects): Live-Sync verstecken zwischen App und Diagnostic (ohne Refresh)
Kern-Bug: der Diagnostic-Server reichte ein von RVS empfangenes project_changed NIE an die Browser weiter — der Handler in index.html war toter Code, der Diagnostic aktualisierte die Projektliste also nie live. - server.js: RVS-Handler forwardet project_changed jetzt an die Browser-Tabs; der /api/brain-Proxy broadcastet project_changed (RVS + lokal) nach jeder erfolgreichen Projekt-Mutation (create/switch/end/archive/PATCH inkl. hidden). - App ProjectsBrowser: sendet project_changed nach dem Verstecken/Sichtbar- machen und laedt bei eingehendem project_changed selbst neu. Damit: verstecken in der App -> sofort im Diagnostic weg (und umgekehrt), ohne Seiten-Refresh. Diagnostic-Hide funktionierte schon (Daten/Proxy/Logik korrekt) — versteckte sind per "Versteckte anzeigen (N)"-Toggle sichtbar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -112,6 +112,17 @@ export const ProjectsBrowser: React.FC<Props> = ({ visible = true, onClose, onAc
|
|||||||
return () => unsub();
|
return () => unsub();
|
||||||
}, [visible, load]);
|
}, [visible, load]);
|
||||||
|
|
||||||
|
// Live-Sync: ein anderer Client (Diagnostic / andere App) hat ein Projekt
|
||||||
|
// geaendert (verstecken/anlegen/beenden/…) → project_changed ueber RVS →
|
||||||
|
// Liste neu laden, ohne dass Stefan manuell refreshen muss.
|
||||||
|
useEffect(() => {
|
||||||
|
if (!visible) return;
|
||||||
|
const unsub = rvs.onMessage((msg: any) => {
|
||||||
|
if (msg?.type === 'project_changed') load();
|
||||||
|
});
|
||||||
|
return () => unsub();
|
||||||
|
}, [visible, load]);
|
||||||
|
|
||||||
const switchTo = useCallback((id: string) => {
|
const switchTo = useCallback((id: string) => {
|
||||||
// Multi-Threading: Focus-Wechsel ist reine App-lokale UI-Entscheidung.
|
// Multi-Threading: Focus-Wechsel ist reine App-lokale UI-Entscheidung.
|
||||||
// Brain wird nicht mehr benachrichtigt (kein globaler active_project mehr).
|
// Brain wird nicht mehr benachrichtigt (kein globaler active_project mehr).
|
||||||
@@ -161,11 +172,18 @@ export const ProjectsBrowser: React.FC<Props> = ({ visible = true, onClose, onAc
|
|||||||
]);
|
]);
|
||||||
}, [load]);
|
}, [load]);
|
||||||
|
|
||||||
|
// Nach einer Projekt-Mutation die anderen Clients (Diagnostic, weitere
|
||||||
|
// App-Instanzen) live aktualisieren — via RVS project_changed. RVS echot
|
||||||
|
// NICHT an den Sender zurueck, darum laden wir lokal zusaetzlich selbst.
|
||||||
|
const broadcastProjectsChanged = useCallback(() => {
|
||||||
|
try { rvs.send('project_changed' as any, { reason: 'app' }); } catch {}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const toggleHidden = useCallback((p: Project) => {
|
const toggleHidden = useCallback((p: Project) => {
|
||||||
brainApi.setProjectHidden(p.id, !p.hidden)
|
brainApi.setProjectHidden(p.id, !p.hidden)
|
||||||
.then(() => load())
|
.then(() => { broadcastProjectsChanged(); load(); })
|
||||||
.catch(e => Alert.alert('Fehler', String(e?.message || e)));
|
.catch(e => Alert.alert('Fehler', String(e?.message || e)));
|
||||||
}, [load]);
|
}, [load, broadcastProjectsChanged]);
|
||||||
|
|
||||||
const archiveProject = useCallback((p: Project) => {
|
const archiveProject = useCallback((p: Project) => {
|
||||||
Alert.alert(`"${p.name}" archivieren?`,
|
Alert.alert(`"${p.name}" archivieren?`,
|
||||||
|
|||||||
@@ -898,6 +898,11 @@ function connectRVS(forcePlain) {
|
|||||||
// Mode-Broadcast von der Bridge → an Browser-Clients weiterreichen
|
// Mode-Broadcast von der Bridge → an Browser-Clients weiterreichen
|
||||||
log("info", "rvs", `Mode-Broadcast: ${msg.payload?.mode} (${msg.payload?.name})`);
|
log("info", "rvs", `Mode-Broadcast: ${msg.payload?.mode} (${msg.payload?.name})`);
|
||||||
broadcast({ type: "mode", payload: msg.payload });
|
broadcast({ type: "mode", payload: msg.payload });
|
||||||
|
} else if (msg.type === "project_changed") {
|
||||||
|
// Ein Projekt wurde geaendert (ARIA-Tool, App-Verstecken, …) → an die
|
||||||
|
// Browser-Tabs weiterreichen, damit die Projektliste live neu laedt
|
||||||
|
// (bisher wurde das NICHT geforwardet → Diagnostic aktualisierte nie).
|
||||||
|
broadcast({ type: "project_changed", payload: msg.payload || {} });
|
||||||
} else if (msg.type === "agent_activity") {
|
} else if (msg.type === "agent_activity") {
|
||||||
// Bridge meldet "ARIA denkt/schreibt/tool" oder "idle" — an Browser
|
// Bridge meldet "ARIA denkt/schreibt/tool" oder "idle" — an Browser
|
||||||
// weiterreichen, damit der Thinking-Indikator im Chat erscheint.
|
// weiterreichen, damit der Thinking-Indikator im Chat erscheint.
|
||||||
@@ -2168,6 +2173,13 @@ const server = http.createServer((req, res) => {
|
|||||||
// mehr als eine Minute.
|
// mehr als eine Minute.
|
||||||
const isUpload = /\/attachments(\/upload)?$/.test(targetPath);
|
const isUpload = /\/attachments(\/upload)?$/.test(targetPath);
|
||||||
const timeout = isUpload ? 120000 : 60000;
|
const timeout = isUpload ? 120000 : 60000;
|
||||||
|
// Projekt-Mutationen (create/switch/end/archive/patch inkl. hidden) sollen
|
||||||
|
// alle Clients live aktualisieren. Wir broadcasten nach Erfolg ein
|
||||||
|
// project_changed an RVS — App + andere Diagnostic-Tabs laden dann neu,
|
||||||
|
// ohne Seiten-Refresh (spiegelt das bestehende ARIA-project_changed-Event).
|
||||||
|
const isProjectMutation =
|
||||||
|
/^\/projects\b/.test(targetPath) &&
|
||||||
|
(req.method === "POST" || req.method === "PATCH" || req.method === "DELETE");
|
||||||
const proxyReq = http.request({
|
const proxyReq = http.request({
|
||||||
host: "aria-brain",
|
host: "aria-brain",
|
||||||
port: 8080,
|
port: 8080,
|
||||||
@@ -2178,6 +2190,17 @@ const server = http.createServer((req, res) => {
|
|||||||
}, (proxyRes) => {
|
}, (proxyRes) => {
|
||||||
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
||||||
proxyRes.pipe(res);
|
proxyRes.pipe(res);
|
||||||
|
if (isProjectMutation && proxyRes.statusCode >= 200 && proxyRes.statusCode < 300) {
|
||||||
|
try {
|
||||||
|
// An App + Bridge (RVS echot NICHT an den Sender) …
|
||||||
|
sendToRVS_raw({ type: "project_changed",
|
||||||
|
payload: { reason: "diagnostic" },
|
||||||
|
timestamp: Date.now() });
|
||||||
|
// … und an die eigenen Browser-Tabs (die haengen am Diag-Server, nicht
|
||||||
|
// direkt am RVS, kriegen den RVS-Broadcast also nicht).
|
||||||
|
broadcast({ type: "project_changed", payload: { reason: "diagnostic" } });
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
proxyReq.on("error", (err) => {
|
proxyReq.on("error", (err) => {
|
||||||
res.writeHead(503, { "Content-Type": "application/json" });
|
res.writeHead(503, { "Content-Type": "application/json" });
|
||||||
|
|||||||
Reference in New Issue
Block a user