|
|
|
@@ -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 '<div class="nn-item"><span>'+esc(name)+'</span><span class="nn-actions">'+actions+'</span></div>'}function render(data){q('[data-nn-incoming]').innerHTML=data.incoming.length?data.incoming.map(n=>item(n,'<button data-action="accept" data-user="'+esc(n)+'">Annehmen</button><button data-action="decline" data-user="'+esc(n)+'">Ablehnen</button>')).join(''):'<span class="mf-small-hint">Keine offenen Anfragen.</span>';q('[data-nn-outgoing]').innerHTML=data.outgoing.length?data.outgoing.map(n=>item(n,'<button data-action="cancel" data-user="'+esc(n)+'">Zurückziehen</button>')).join(''):'<span class="mf-small-hint">Keine gesendeten Anfragen.</span>';q('[data-nn-connections]').innerHTML=data.connections.length?data.connections.map(n=>item(n,'<button data-action="chat" data-user="'+esc(n)+'" class="'+(selected===n?'active':'')+'">Chat</button>')).join(''):'<span class="mf-small-hint">Noch keine Verbindungen.</span>'}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=>'<article class="nn-message '+(m.sender===me?'mine':'')+'">'+esc(m.body)+'<small>'+esc(m.sender)+' · '+new Date(m.sentAt).toLocaleString('de-DE',{dateStyle:'short',timeStyle:'short'})+'</small></article>').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 '<div class="nn-item"><span>' + esc(name) + '</span><span class="nn-actions">' + actions + '</span></div>';
|
|
|
|
|
}
|
|
|
|
|
function render(data) {
|
|
|
|
|
q('[data-nn-incoming]').innerHTML = data.incoming.length ? data.incoming.map(function (name) {
|
|
|
|
|
return item(name, '<button data-action="accept" data-user="' + esc(name) + '">Annehmen</button><button data-action="decline" data-user="' + esc(name) + '">Ablehnen</button>');
|
|
|
|
|
}).join('') : '<span class="mf-small-hint">Keine offenen Anfragen.</span>';
|
|
|
|
|
q('[data-nn-outgoing]').innerHTML = data.outgoing.length ? data.outgoing.map(function (name) {
|
|
|
|
|
return item(name, '<button data-action="cancel" data-user="' + esc(name) + '">Zurückziehen</button>');
|
|
|
|
|
}).join('') : '<span class="mf-small-hint">Keine gesendeten Anfragen.</span>';
|
|
|
|
|
q('[data-nn-connections]').innerHTML = data.connections.length ? data.connections.map(function (name) {
|
|
|
|
|
return item(name, '<button data-action="chat" data-user="' + esc(name) + '" class="' + (selected === name ? 'active' : '') + '">Chat</button>');
|
|
|
|
|
}).join('') : '<span class="mf-small-hint">Noch keine Verbindungen.</span>';
|
|
|
|
|
}
|
|
|
|
|
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 '<article class="nn-message ' + (message.sender === me ? 'mine' : '') + '">' + esc(message.body) + '<small>' + esc(message.sender) + ' · ' + new Date(message.sentAt).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' }) + '</small></article>';
|
|
|
|
|
}).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();
|
|
|
|
|
})();
|
|
|
|
|