From fb487be0377a5f75628c3d9d7fdd1b6acb4fad51 Mon Sep 17 00:00:00 2001 From: AliceDarkest Date: Tue, 14 Jul 2026 16:13:17 +0200 Subject: [PATCH] Fix NeuralNet empty API responses --- CHANGELOG.md | 3 + mindfluxfuture.md | 1 + src/main/resources/static/js/neuralnet.js | 100 ++++++++++++++++++++- src/main/resources/templates/soziales.html | 2 +- 4 files changed, 104 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee18f11..e949992 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ Versionierung [semantisch](https://semver.org/lang/de/). ## [Unreleased] – 2026-07-14 · SysnaptiK Neural Rebrand ### Marke & Plattform +- **NeuralNet-Chat repariert:** Leere, erfolgreiche API-Antworten werden nicht mehr + fälschlich als JSON gelesen. Kontaktaktionen aktualisieren die Oberfläche wieder + zuverlässig und bestätigte Verbindungen lassen sich direkt im Chat öffnen. - **NeuralNet-Testkonto:** Ein klar benanntes, für NeuralNet aktiviertes Dummykonto erleichtert das Prüfen von Kontaktanfragen und privaten Nachrichten. - **NeuralNet-MVP:** Der frühere Soziales-Platzhalter ist jetzt ein Opt-in-Hub für diff --git a/mindfluxfuture.md b/mindfluxfuture.md index 9e09474..2b34e3b 100644 --- a/mindfluxfuture.md +++ b/mindfluxfuture.md @@ -488,6 +488,7 @@ Der Verdacht „Profilbutton fehlt / Einstellungen nicht im Profil / irgendwas i > ✅ **MVP-Stand 2026-07-14:** Opt-in, Kontaktanfragen (annehmen, ablehnen, > zurückziehen) und private Nachrichten nach bestätigter Verbindung sind umgesetzt. +> Leere Erfolgsantworten des API-Backends werden im Client robust verarbeitet. > Blockieren, Melden/Moderation, Duelle und Lernlobby bleiben die nächsten Ausbauschritte. > Ersetzt den `/soziales`-Platzhalter („Straßen? Wo wir hingehen, brauchen wir keine Straßen!"). Ziel: User können sich befreunden, intern miteinander schreiben, gemeinsam lernen und Achievements vergleichen – **vollständig selbst gehostet, ohne einen einzigen externen Dienst**, und DSGVO-konform von Grund auf. diff --git a/src/main/resources/static/js/neuralnet.js b/src/main/resources/static/js/neuralnet.js index 5723c91..557cca6 100644 --- a/src/main/resources/static/js/neuralnet.js +++ b/src/main/resources/static/js/neuralnet.js @@ -1 +1,99 @@ -(function(){'use strict';var selected=null,me='',poll=null;function q(s){return document.querySelector(s)}function esc(s){return String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))}async function api(url,method,body){var r=await fetch('/api/neuralnet'+url,{method:method||'GET',headers:{'Content-Type':'application/json'},body:body?JSON.stringify(body):undefined});if(!r.ok){var e=await r.json().catch(()=>({message:'Anfrage fehlgeschlagen.'}));throw new Error(e.message)}return r.status===204?null:r.json()}function item(name,actions){return '
'+esc(name)+''+actions+'
'}function render(data){q('[data-nn-incoming]').innerHTML=data.incoming.length?data.incoming.map(n=>item(n,'')).join(''):'Keine offenen Anfragen.';q('[data-nn-outgoing]').innerHTML=data.outgoing.length?data.outgoing.map(n=>item(n,'')).join(''):'Keine gesendeten Anfragen.';q('[data-nn-connections]').innerHTML=data.connections.length?data.connections.map(n=>item(n,'')).join(''):'Noch keine Verbindungen.'}async function refresh(){var data=await api('/overview');me=data.username;q('#nnOptIn').hidden=data.enabled;q('#nnApp').hidden=!data.enabled;if(data.enabled)render(data)}async function loadChat(){if(!selected)return;var rows=await api('/messages/'+encodeURIComponent(selected));q('[data-nn-empty]').hidden=true;q('[data-nn-conversation]').hidden=false;q('[data-nn-partner]').textContent=selected;q('[data-nn-messages]').innerHTML=rows.map(m=>'
'+esc(m.body)+''+esc(m.sender)+' · '+new Date(m.sentAt).toLocaleString('de-DE',{dateStyle:'short',timeStyle:'short'})+'
').join('');var box=q('[data-nn-messages]');box.scrollTop=box.scrollHeight}function message(text){var old=q('.nn-head p');old.textContent=text;setTimeout(()=>old.textContent='Erst eine bestätigte Anfrage, dann ein geschützter Nachrichtenkanal.',3500)}function init(){q('[data-nn-enable]').onclick=async()=>{await api('/enable','POST');refresh()};q('[data-nn-request]').onsubmit=async e=>{e.preventDefault();try{await api('/requests','POST',{username:q('#nn-user').value});q('#nn-user').value='';message('Kontaktanfrage gesendet.')}catch(x){message(x.message)}refresh()};q('.nn-sidebar').onclick=async e=>{var b=e.target.closest('[data-action]');if(!b)return;var u=b.dataset.user,a=b.dataset.action;try{if(a==='chat'){selected=u;await refresh();await loadChat();return}if(a==='accept')await api('/requests/'+encodeURIComponent(u)+'/accept','POST');if(a==='decline')await api('/requests/'+encodeURIComponent(u)+'/decline','POST');if(a==='cancel')await api('/requests/'+encodeURIComponent(u),'DELETE');await refresh()}catch(x){message(x.message)}};q('[data-nn-compose]').onsubmit=async e=>{e.preventDefault();try{await api('/messages','POST',{recipient:selected,body:q('#nn-message').value});q('#nn-message').value='';loadChat()}catch(x){message(x.message)}};refresh().catch(x=>message(x.message));poll=setInterval(()=>{refresh();if(selected)loadChat()},5000)}if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init()})(); +(function () { + 'use strict'; + + var selected = null, me = '', poll = null; + function q(selector) { return document.querySelector(selector); } + function esc(value) { + return String(value).replace(/[&<>"']/g, function (character) { + return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[character]; + }); + } + + async function api(url, method, body) { + var response = await fetch('/api/neuralnet' + url, { + method: method || 'GET', + headers: { 'Content-Type': 'application/json' }, + body: body ? JSON.stringify(body) : undefined + }); + var text = await response.text(), data = null; + if (text) { + try { data = JSON.parse(text); } catch (ignored) { data = null; } + } + if (!response.ok) throw new Error((data && data.message) || 'Anfrage fehlgeschlagen.'); + return data; + } + + function item(name, actions) { + return '
' + esc(name) + '' + actions + '
'; + } + function render(data) { + q('[data-nn-incoming]').innerHTML = data.incoming.length ? data.incoming.map(function (name) { + return item(name, ''); + }).join('') : 'Keine offenen Anfragen.'; + q('[data-nn-outgoing]').innerHTML = data.outgoing.length ? data.outgoing.map(function (name) { + return item(name, ''); + }).join('') : 'Keine gesendeten Anfragen.'; + q('[data-nn-connections]').innerHTML = data.connections.length ? data.connections.map(function (name) { + return item(name, ''); + }).join('') : 'Noch keine Verbindungen.'; + } + async function refresh() { + var data = await api('/overview'); + me = data.username; + q('#nnOptIn').hidden = data.enabled; + q('#nnApp').hidden = !data.enabled; + if (data.enabled) render(data); + } + async function loadChat() { + if (!selected) return; + var rows = await api('/messages/' + encodeURIComponent(selected)); + q('[data-nn-empty]').hidden = true; + q('[data-nn-conversation]').hidden = false; + q('[data-nn-partner]').textContent = selected; + q('[data-nn-messages]').innerHTML = rows.map(function (message) { + return '
' + esc(message.body) + '' + esc(message.sender) + ' · ' + new Date(message.sentAt).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' }) + '
'; + }).join(''); + var box = q('[data-nn-messages]'); + box.scrollTop = box.scrollHeight; + } + function message(text) { + var hint = q('.nn-head p'); + hint.textContent = text; + setTimeout(function () { hint.textContent = 'Erst eine bestätigte Anfrage, dann ein geschützter Nachrichtenkanal.'; }, 3500); + } + function init() { + q('[data-nn-enable]').onclick = async function () { await api('/enable', 'POST'); refresh(); }; + q('[data-nn-request]').onsubmit = async function (event) { + event.preventDefault(); + try { + await api('/requests', 'POST', { username: q('#nn-user').value }); + q('#nn-user').value = ''; + message('Kontaktanfrage gesendet.'); + } catch (error) { message(error.message); } + refresh(); + }; + q('.nn-sidebar').onclick = async function (event) { + var button = event.target.closest('[data-action]'); + if (!button) return; + var user = button.dataset.user, action = button.dataset.action; + try { + if (action === 'chat') { selected = user; await refresh(); await loadChat(); return; } + if (action === 'accept') await api('/requests/' + encodeURIComponent(user) + '/accept', 'POST'); + if (action === 'decline') await api('/requests/' + encodeURIComponent(user) + '/decline', 'POST'); + if (action === 'cancel') await api('/requests/' + encodeURIComponent(user), 'DELETE'); + await refresh(); + } catch (error) { message(error.message); } + }; + q('[data-nn-compose]').onsubmit = async function (event) { + event.preventDefault(); + try { + await api('/messages', 'POST', { recipient: selected, body: q('#nn-message').value }); + q('#nn-message').value = ''; + loadChat(); + } catch (error) { message(error.message); } + }; + refresh().catch(function (error) { message(error.message); }); + poll = setInterval(function () { refresh(); if (selected) loadChat(); }, 5000); + } + if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init(); +})(); diff --git a/src/main/resources/templates/soziales.html b/src/main/resources/templates/soziales.html index 804744a..f65f3f1 100644 --- a/src/main/resources/templates/soziales.html +++ b/src/main/resources/templates/soziales.html @@ -4,7 +4,7 @@ NeuralNet – SysnaptiK Neural - +