filename:
views/api/upload_interactions.php
branch:
main
back to repo
<?php
declare(strict_types=1);
// This endpoint is used by the modal via HTML requests (no JSON API).
// It returns ONLY the modal body so the frontend can swap it into the modal.
// Allow GET to be public (counts/comments). Reactions/comments require login.
$uploadId = (int)($_GET['upload_id'] ?? $_POST['upload_id'] ?? 0);
if ($uploadId <= 0) {
http_response_code(400);
echo 'Missing upload_id.';
return;
}
$currentUserId = currentUserId();
$reactionAction = $_POST['reaction'] ?? null; // kept for backward compatibility; modal now returns comments only
$commentBody = $_POST['comment_body'] ?? null;
$isAjax = ($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') === 'XMLHttpRequest';
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Determine which action this request is for.
if ($reactionAction !== null) {
if (!isLoggedIn()) {
http_response_code(401);
echo 'Sign in required.';
return;
}
$reaction = $_POST['reaction'] ?? '';
if ($reaction !== 'like' && $reaction !== 'dislike') {
$errors[] = 'Invalid reaction.';
} else {
$userId = (int)currentUserId();
$nowStr = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
// Upsert: unique key (upload_id,user_id) ensures one row per user.
$stmt = $db->prepare('
INSERT INTO upload_reactions (upload_id, user_id, reaction, created_at)
VALUES (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE reaction = VALUES(reaction), created_at = VALUES(created_at)
');
$stmt->bind_param('iiss', $uploadId, $userId, $reaction, $nowStr);
$stmt->execute();
}
// For async reaction posts from the main page, return JSON counts.
if ($isAjax) {
$counts = $db->prepare('
SELECT
(SELECT COUNT(*) FROM upload_reactions r WHERE r.upload_id = ? AND r.reaction = "like") AS like_count,
(SELECT COUNT(*) FROM upload_reactions r WHERE r.upload_id = ? AND r.reaction = "dislike") AS dislike_count
');
$counts->bind_param('ii', $uploadId, $uploadId);
$counts->execute();
$countsRow = $counts->get_result()->fetch_assoc();
$myReaction = null;
if (isLoggedIn()) {
$stmt2 = $db->prepare('SELECT reaction FROM upload_reactions WHERE upload_id = ? AND user_id = ? LIMIT 1');
$uid = (int)currentUserId();
$stmt2->bind_param('ii', $uploadId, $uid);
$stmt2->execute();
$row2 = $stmt2->get_result()->fetch_assoc();
$myReaction = $row2['reaction'] ?? null;
}
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'like_count' => (int)($countsRow['like_count'] ?? 0),
'dislike_count' => (int)($countsRow['dislike_count'] ?? 0),
'my_reaction' => $myReaction,
]);
return;
}
} elseif ($commentBody !== null) {
if (!isLoggedIn()) {
http_response_code(401);
echo 'Sign in required.';
return;
}
$body = trim((string)$commentBody);
if ($body === '') {
$errors[] = 'Comment cannot be empty.';
} else {
$userId = (int)currentUserId();
$nowStr = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
$stmt = $db->prepare('INSERT INTO upload_comments (upload_id, user_id, body, created_at) VALUES (?, ?, ?, ?)');
$stmt->bind_param('iiss', $uploadId, $userId, $body, $nowStr);
$stmt->execute();
}
} else {
http_response_code(400);
echo 'Unsupported action.';
return;
}
}
// Comments (newest-first)
$commentsStmt = $db->prepare('
SELECT c.id, c.user_id, c.body, c.created_at, mp.display_name
FROM upload_comments c
JOIN member_profiles mp ON mp.user_id = c.user_id
WHERE c.upload_id = ?
ORDER BY c.created_at DESC, c.id DESC
');
$commentsStmt->bind_param('i', $uploadId);
$commentsStmt->execute();
$comments = $commentsStmt->get_result()->fetch_all(MYSQLI_ASSOC);
?>
<div class="upload-interactions-modal-body">
<div style="margin-bottom: 10px;">
<?php if ($errors): ?>
<div style="color: #9b2c2c; font-size: 13px; margin-bottom: 10px;">
<?php foreach ($errors as $err): ?>
<div><?= htmlspecialchars($err, ENT_QUOTES, 'UTF-8') ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="muted upload-interactions-comments-heading" style="font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; margin-bottom: 8px;">
Comments
</div>
<?php if (!isLoggedIn()): ?>
<div style="font-size: 13px; color: var(--muted); margin-bottom: 12px;">
Sign in to leave a comment.
</div>
<?php else: ?>
<form method="post" action="<?= htmlspecialchars(url('api/upload-interactions'), ENT_QUOTES, 'UTF-8') ?>" class="upload-comment-form" style="display:grid; gap: 8px; margin-bottom: 14px;">
<input type="hidden" name="upload_id" value="<?= (int)$uploadId ?>">
<label>
<span class="muted" style="font-size: 12px;">Add a comment</span><br>
<textarea name="comment_body" rows="3" required style="width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);"></textarea>
</label>
<button type="submit" class="pill pill-accent" style="justify-content:center; max-width: 160px;">
Post Comment
</button>
</form>
<?php endif; ?>
</div>
<?php if (!$comments): ?>
<div style="font-size: 13px; color: var(--muted);">
No comments yet.
</div>
<?php else: ?>
<div style="display:grid; gap: 10px;">
<?php foreach ($comments as $c): ?>
<div style="border-radius: 14px; border: 1px solid rgba(0,0,0,0.06); background: rgba(255,255,255,0.75); padding: 10px 12px;">
<div style="display:flex; justify-content: space-between; gap: 12px; margin-bottom: 6px;">
<div style="font-size: 12px; color: var(--muted);">
<?= htmlspecialchars($c['display_name'], ENT_QUOTES, 'UTF-8') ?>
</div>
<div style="font-size: 11px; color: rgba(0,0,0,0.45);">
<?= htmlspecialchars(date('M j, Y', strtotime($c['created_at'])), ENT_QUOTES, 'UTF-8') ?>
</div>
</div>
<div style="font-size: 13px; line-height: 1.5;">
<?= nl2br(htmlspecialchars($c['body'], ENT_QUOTES, 'UTF-8')) ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>