Public: render newly submitted comments without page reload

This commit is contained in:
Alexander Andreev 2026-02-21 16:07:05 +03:00
parent 803ab55c37
commit 2760e46a17
2 changed files with 82 additions and 8 deletions

View File

@ -27,13 +27,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') ==
|| str_contains((string)($_SERVER['HTTP_ACCEPT'] ?? ''), 'application/json'); || str_contains((string)($_SERVER['HTTP_ACCEPT'] ?? ''), 'application/json');
$commentSaved = false; $commentSaved = false;
$savedComment = null;
$errorMessage = ''; $errorMessage = '';
$errorCode = 422;
if ($token !== '' && $photoId > 0 && $text !== '') { if ($token !== '' && $photoId > 0 && $text !== '') {
$u = commenterByToken($token); $u = commenterByToken($token);
if ($u) { if ($u) {
commentAdd($photoId, (int)$u['id'], limitText($text, 1000)); try {
$commentSaved = true; $savedComment = commentAdd($photoId, (int)$u['id'], limitText($text, 1000));
$commentSaved = true;
} catch (Throwable) {
$errorMessage = 'Не удалось отправить комментарий.';
$errorCode = 500;
}
} else { } else {
$errorMessage = 'Ссылка для комментариев недействительна.'; $errorMessage = 'Ссылка для комментариев недействительна.';
} }
@ -44,11 +51,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') ==
if ($isAjax) { if ($isAjax) {
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
if ($commentSaved) { if ($commentSaved) {
echo json_encode(['ok' => true, 'message' => 'Ваш комментарий отправлен.'], JSON_UNESCAPED_UNICODE); echo json_encode([
'ok' => true,
'message' => 'Ваш комментарий отправлен.',
'comment' => $savedComment ? [
'id' => (int)($savedComment['id'] ?? 0),
'comment_text' => (string)($savedComment['comment_text'] ?? ''),
'created_at' => (string)($savedComment['created_at'] ?? ''),
'display_name' => (string)($savedComment['display_name'] ?? 'Пользователь'),
] : null,
], JSON_UNESCAPED_UNICODE);
exit; exit;
} }
http_response_code(422); http_response_code($errorCode);
echo json_encode(['ok' => false, 'message' => $errorMessage !== '' ? $errorMessage : 'Не удалось отправить комментарий.'], JSON_UNESCAPED_UNICODE); echo json_encode(['ok' => false, 'message' => $errorMessage !== '' ? $errorMessage : 'Не удалось отправить комментарий.'], JSON_UNESCAPED_UNICODE);
exit; exit;
} }
@ -688,9 +704,11 @@ function outputWatermarked(string $path, string $mime): never
<p class="muted">Комментарии может оставлять только пользователь с персональной ссылкой.</p> <p class="muted">Комментарии может оставлять только пользователь с персональной ссылкой.</p>
<?php endif; ?> <?php endif; ?>
<?php foreach($comments as $c): ?> <div class="js-comments-list">
<div class="cmt"><strong><?= h((string)($c['display_name'] ?? 'Пользователь')) ?></strong> <span class="muted">· <?= h((string)$c['created_at']) ?></span><br><?= nl2br(h((string)$c['comment_text'])) ?></div> <?php foreach($comments as $c): ?>
<?php endforeach; ?> <div class="cmt"><strong><?= h((string)($c['display_name'] ?? 'Пользователь')) ?></strong> <span class="muted">· <?= h((string)$c['created_at']) ?></span><br><?= nl2br(h((string)$c['comment_text'])) ?></div>
<?php endforeach; ?>
</div>
<?php if ($detailTotal > 0): ?> <?php if ($detailTotal > 0): ?>
<div class="pager"> <div class="pager">
@ -889,6 +907,38 @@ function outputWatermarked(string $path, string $mime): never
const commentForm = commentTextarea ? commentTextarea.closest('.js-comment-form') : null; const commentForm = commentTextarea ? commentTextarea.closest('.js-comment-form') : null;
const commentFeedback = commentForm ? commentForm.querySelector('.js-comment-feedback') : null; const commentFeedback = commentForm ? commentForm.querySelector('.js-comment-feedback') : null;
const commentSubmitButton = commentForm ? commentForm.querySelector('button[type="submit"]') : null; const commentSubmitButton = commentForm ? commentForm.querySelector('button[type="submit"]') : null;
const commentsList = document.querySelector('.js-comments-list');
const prependComment = (comment) => {
if (!commentsList || !comment || typeof comment !== 'object') {
return;
}
const item = document.createElement('div');
item.className = 'cmt';
const author = document.createElement('strong');
author.textContent = String(comment.display_name || 'Пользователь');
const meta = document.createElement('span');
meta.className = 'muted';
meta.textContent = `· ${String(comment.created_at || '')}`;
item.appendChild(author);
item.append(' ');
item.appendChild(meta);
item.appendChild(document.createElement('br'));
const lines = String(comment.comment_text || '').split(/\r\n|\r|\n/);
lines.forEach((line, index) => {
item.append(document.createTextNode(line));
if (index < lines.length - 1) {
item.appendChild(document.createElement('br'));
}
});
commentsList.prepend(item);
};
const setCommentFeedback = (message, isError) => { const setCommentFeedback = (message, isError) => {
if (!commentFeedback) { if (!commentFeedback) {
@ -960,6 +1010,9 @@ function outputWatermarked(string $path, string $mime): never
} }
setCommentFeedback(payload.message || 'Ваш комментарий отправлен.', false); setCommentFeedback(payload.message || 'Ваш комментарий отправлен.', false);
if (payload.comment) {
prependComment(payload.comment);
}
commentTextarea.value = ''; commentTextarea.value = '';
commentTextarea.focus(); commentTextarea.focus();
} catch (error) { } catch (error) {

View File

@ -360,10 +360,31 @@ function commentsByPhoto(int $photoId): array
return $st->fetchAll(); return $st->fetchAll();
} }
function commentAdd(int $photoId, int $userId, string $text): void function commentAdd(int $photoId, int $userId, string $text): array
{ {
$st = db()->prepare('INSERT INTO photo_comments(photo_id, user_id, comment_text) VALUES (:p,:u,:t)'); $st = db()->prepare('INSERT INTO photo_comments(photo_id, user_id, comment_text) VALUES (:p,:u,:t)');
$st->execute(['p' => $photoId, 'u' => $userId, 't' => $text]); $st->execute(['p' => $photoId, 'u' => $userId, 't' => $text]);
$commentId = (int)db()->lastInsertId();
$detail = db()->prepare('SELECT c.*, u.display_name
FROM photo_comments c
LEFT JOIN comment_users u ON u.id=c.user_id
WHERE c.id=:id');
$detail->execute(['id' => $commentId]);
$row = $detail->fetch();
if (!$row) {
return [
'id' => $commentId,
'photo_id' => $photoId,
'user_id' => $userId,
'comment_text' => $text,
'created_at' => date('Y-m-d H:i:s'),
'display_name' => 'Пользователь',
];
}
return $row;
} }
function commentDelete(int $id): void function commentDelete(int $id): void