feat: Implement pinned comments, locked comment threads, and a two-level reply structure with @mentions.
This commit is contained in:
@@ -14,13 +14,14 @@ export default (router, tpl) => {
|
||||
const comments = await db`
|
||||
SELECT
|
||||
c.id, c.parent_id, c.content, c.created_at, c.vote_score, c.is_deleted,
|
||||
COALESCE(c.is_pinned, false) as is_pinned,
|
||||
u.user as username, u.id as user_id, uo.avatar,
|
||||
(SELECT count(*) FROM comments r WHERE r.parent_id = c.id) as reply_count
|
||||
FROM comments c
|
||||
JOIN "user" u ON c.user_id = u.id
|
||||
LEFT JOIN user_options uo ON uo.user_id = u.id
|
||||
WHERE c.item_id = ${itemId}
|
||||
ORDER BY c.created_at ${db.unsafe(sort === 'new' ? 'DESC' : 'ASC')}
|
||||
ORDER BY COALESCE(c.is_pinned, false) DESC, c.created_at ${db.unsafe(sort === 'new' ? 'DESC' : 'ASC')}
|
||||
`;
|
||||
|
||||
let is_subscribed = false;
|
||||
@@ -29,6 +30,10 @@ export default (router, tpl) => {
|
||||
if (sub.length > 0) is_subscribed = true;
|
||||
}
|
||||
|
||||
// Check if thread is locked
|
||||
const itemInfo = await db`SELECT COALESCE(is_comments_locked, false) as is_locked FROM items WHERE id = ${itemId}`;
|
||||
const is_locked = itemInfo.length > 0 ? itemInfo[0].is_locked : false;
|
||||
|
||||
// Transform for frontend if needed, or send as is
|
||||
return res.reply({
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -36,6 +41,7 @@ export default (router, tpl) => {
|
||||
success: true,
|
||||
comments,
|
||||
is_subscribed,
|
||||
is_locked,
|
||||
user_id: req.session ? req.session.user : null,
|
||||
is_admin: req.session ? req.session.admin : false
|
||||
})
|
||||
@@ -68,6 +74,14 @@ export default (router, tpl) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if thread is locked (admins can still post)
|
||||
if (!req.session.admin) {
|
||||
const lockCheck = await db`SELECT COALESCE(is_comments_locked, false) as is_locked FROM items WHERE id = ${item_id}`;
|
||||
if (lockCheck.length > 0 && lockCheck[0].is_locked) {
|
||||
return res.reply({ code: 403, body: JSON.stringify({ success: false, message: "This thread is locked" }) });
|
||||
}
|
||||
}
|
||||
|
||||
const newComment = await db`
|
||||
INSERT INTO comments ${db({
|
||||
item_id,
|
||||
@@ -209,6 +223,52 @@ export default (router, tpl) => {
|
||||
return res.reply({ code: 500, body: JSON.stringify({ success: false }) });
|
||||
}
|
||||
});
|
||||
// Toggle pin comment (admin only)
|
||||
router.post(/\/api\/comments\/(?<id>\d+)\/pin/, async (req, res) => {
|
||||
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false }) });
|
||||
if (!req.session.admin) return res.reply({ code: 403, body: JSON.stringify({ success: false, message: "Admin only" }) });
|
||||
|
||||
const commentId = req.params.id;
|
||||
|
||||
try {
|
||||
const comment = await db`SELECT id, COALESCE(is_pinned, false) as is_pinned FROM comments WHERE id = ${commentId}`;
|
||||
if (!comment.length) return res.reply({ code: 404, body: JSON.stringify({ success: false, message: "Not found" }) });
|
||||
|
||||
const newPinned = !comment[0].is_pinned;
|
||||
await db`UPDATE comments SET is_pinned = ${newPinned} WHERE id = ${commentId}`;
|
||||
|
||||
return res.reply({
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ success: true, is_pinned: newPinned })
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return res.reply({ code: 500, body: JSON.stringify({ success: false }) });
|
||||
}
|
||||
});
|
||||
// Toggle lock thread (admin only)
|
||||
router.post(/\/api\/comments\/(?<itemid>\d+)\/lock/, async (req, res) => {
|
||||
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false }) });
|
||||
if (!req.session.admin) return res.reply({ code: 403, body: JSON.stringify({ success: false, message: "Admin only" }) });
|
||||
|
||||
const itemId = req.params.itemid;
|
||||
|
||||
try {
|
||||
const item = await db`SELECT id, COALESCE(is_comments_locked, false) as is_locked FROM items WHERE id = ${itemId}`;
|
||||
if (!item.length) return res.reply({ code: 404, body: JSON.stringify({ success: false, message: "Not found" }) });
|
||||
|
||||
const newLocked = !item[0].is_locked;
|
||||
await db`UPDATE items SET is_comments_locked = ${newLocked} WHERE id = ${itemId}`;
|
||||
|
||||
return res.reply({
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ success: true, is_locked: newLocked })
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return res.reply({ code: 500, body: JSON.stringify({ success: false }) });
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user