diff --git a/DeskHub-Webseite/staff/assistant.php b/DeskHub-Webseite/staff/assistant.php
index 66a714fa..1f398724 100644
--- a/DeskHub-Webseite/staff/assistant.php
+++ b/DeskHub-Webseite/staff/assistant.php
@@ -320,7 +320,7 @@ $tabs = ['settings' => 'Einstellungen', 'answers' => 'Zusätzliche Antworten' .
$label): ?>
diff --git a/DeskHub-Webseite/staff/chat.php b/DeskHub-Webseite/staff/chat.php
index fc07af9f..fdffc469 100644
--- a/DeskHub-Webseite/staff/chat.php
+++ b/DeskHub-Webseite/staff/chat.php
@@ -60,21 +60,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
redirect('/staff/chat.php?with=' . $toId);
}
+function chatAvClass(string $name): string {
+ $l = strtolower(mb_substr($name, 0, 1));
+ return 'av-' . (ctype_alpha($l) ? $l : 'a');
+}
+
// AJAX: neue Nachrichten pollen
if (($_GET['action'] ?? '') === 'poll' && $withId) {
header('Content-Type: application/json');
- $since = $_GET['since'] ?? '1970-01-01 00:00:00';
+ $after = max(0, (int) ($_GET['after'] ?? 0));
$pStmt = $pdo->prepare(
"SELECT m.*, sf.name AS from_name FROM staff_chat_messages m
JOIN staff sf ON sf.id = m.from_staff_id
WHERE ((m.from_staff_id=? AND m.to_staff_id=?) OR (m.from_staff_id=? AND m.to_staff_id=?))
- AND m.created_at > ?
- ORDER BY m.created_at ASC"
+ AND m.id > ?
+ ORDER BY m.id ASC"
);
- $pStmt->execute([$staff['id'], $withId, $withId, $staff['id'], $since]);
+ $pStmt->execute([$staff['id'], $withId, $withId, $staff['id'], $after]);
$pdo->prepare('UPDATE staff_chat_messages SET is_read=1 WHERE from_staff_id=? AND to_staff_id=?')
->execute([$withId, $staff['id']]);
- echo json_encode($pStmt->fetchAll());
+ // Anzeige-Angaben gleich mitliefern: Das Gerüst im Browser baut die Blase genau wie die Seite selbst
+ echo json_encode(array_map(fn(array $m) => [
+ 'id' => (int) $m['id'], 'from_staff_id' => (int) $m['from_staff_id'], 'from_name' => $m['from_name'], 'message' => $m['message'],
+ 'created_at' => $m['created_at'], 'initial' => mb_strtoupper(mb_substr($m['from_name'], 0, 1)), 'av_class' => chatAvClass($m['from_name']),
+ ], $pStmt->fetchAll()));
exit;
}
@@ -138,10 +147,6 @@ require __DIR__ . '/_header.php';
@@ -173,7 +178,7 @@ function chatAvClass(string $name): string {
-
Noch keine Nachrichten – schreib etwas!
+
Noch keine Nachrichten – schreib etwas!
= date('d.m.Y', strtotime($date)) ?>
-
+
= e($ini) ?>
@@ -254,7 +259,7 @@ var withId = = $withId ?>;
var csrfToken = document.querySelector('[name=csrf_token]').value;
// Token nach Rotation aktualisieren
function getCsrf() { return document.querySelector('[name=csrf_token]').value; }
-var lastTs = = $conversation ? json_encode(end($conversation)['created_at']) : json_encode('1970-01-01 00:00:00') ?>;
+var lastId = = $conversation ? (int) end($conversation)['id'] : 0 ?>;
function insertEmoji(em) {
var input = document.getElementById('chat-input');
@@ -279,14 +284,52 @@ function scrollBottom() {
scrollBottom();
function appendBubble(msg) {
+ var box = document.getElementById('chat-messages');
+ if (box.querySelector('[data-id="' + msg.id + '"]')) return; // schon da
+ var empty = document.getElementById('chat-empty');
+ if (empty) empty.remove();
var isMe = msg.from_staff_id == myId;
- var div = document.createElement('div');
- div.className = 'chat-bubble ' + (isMe ? 'me' : 'them');
- div.dataset.ts = msg.created_at;
- div.innerHTML = '
' + msg.message.replace(/&/g,'&').replace(/') + '
'
- + '
' + msg.created_at + '
';
- document.getElementById('chat-messages').appendChild(div);
- lastTs = msg.created_at;
+ var all = box.querySelectorAll('.ac-msg');
+ var last = all.length ? all[all.length - 1] : null;
+ var day = msg.created_at.slice(0, 10);
+ if (!last || last.dataset.ts.slice(0, 10) !== day) {
+ var sep = document.createElement('div');
+ sep.style.cssText = 'text-align:center;font-size:.72rem;color:#9ca3af;padding:12px 0';
+ sep.textContent = day.slice(8, 10) + '.' + day.slice(5, 7) + '.' + day.slice(0, 4);
+ box.appendChild(sep);
+ }
+ var row = document.createElement('div');
+ row.className = 'ac-msg ' + (isMe ? 'me' : 'them');
+ row.dataset.id = msg.id;
+ row.dataset.ts = msg.created_at;
+ if (!isMe) {
+ var av = document.createElement('div');
+ av.className = 'ac-msg-av ' + msg.av_class;
+ av.textContent = msg.initial;
+ row.appendChild(av);
+ }
+ var body = document.createElement('div');
+ body.className = 'ac-msg-body';
+ if (!isMe) {
+ var name = document.createElement('div');
+ name.className = 'ac-msg-name';
+ name.textContent = msg.from_name;
+ body.appendChild(name);
+ }
+ var bubble = document.createElement('div');
+ bubble.className = 'ac-msg-bubble';
+ msg.message.split('\n').forEach(function (line, i) {
+ if (i) bubble.appendChild(document.createElement('br'));
+ bubble.appendChild(document.createTextNode(line));
+ });
+ body.appendChild(bubble);
+ var time = document.createElement('div');
+ time.className = 'ac-msg-time';
+ time.textContent = msg.created_at.slice(11, 16);
+ body.appendChild(time);
+ row.appendChild(body);
+ box.appendChild(row);
+ if (msg.id > lastId) lastId = msg.id;
scrollBottom();
}
@@ -300,23 +343,19 @@ function sendMsg() {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded','X-Requested-With':'XMLHttpRequest'},
body: 'csrf_token=' + encodeURIComponent(getCsrf()) + '&to_id=' + withId + '&message=' + encodeURIComponent(msg)
- }).then(function() {
- appendBubble({from_staff_id: myId, message: msg, created_at: new Date().toISOString().slice(0,19).replace('T',' ')});
- poll();
- });
+ }).then(function() { poll(); }); // die eigene Nachricht kommt mit Nummer und Serverzeit zurück
}
+var polling = false;
function poll() {
- fetch('/staff/chat.php?with=' + withId + '&action=poll&since=' + encodeURIComponent(lastTs))
+ if (polling) return;
+ polling = true;
+ fetch('/staff/chat.php?with=' + withId + '&action=poll&after=' + lastId)
.then(function(r){ return r.json(); })
- .then(function(msgs) {
- msgs.forEach(function(m) {
- if (m.from_staff_id != myId) appendBubble(m);
- lastTs = m.created_at;
- });
- });
+ .then(function(msgs) { msgs.forEach(appendBubble); })
+ .catch(function() {})
+ .then(function() { polling = false; });
}
-
setInterval(poll, 8000);