feat: Implement server-side comment preloading for improved performance and introduce new comment features including pinned comments, locked threads, and custom emojis.

This commit is contained in:
x
2026-01-25 15:12:57 +01:00
parent 68011c60de
commit bf5996d2ba
6 changed files with 108 additions and 15 deletions

View File

@@ -270,9 +270,37 @@ export default {
const link = lib.genLink({ user, tag, mime, type: o.fav ? 'favs' : 'f0cks' });
return {
success: true,
link: link,
itemid: item[0].id
};
},
getComments: async (itemId, sort = 'new') => {
if (!itemId) return [];
try {
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} AND c.is_deleted = false
ORDER BY COALESCE(c.is_pinned, false) DESC, c.created_at ${db.unsafe(sort === 'new' ? 'DESC' : 'ASC')}
`;
return comments;
} catch (e) {
console.error('[F0CKLIB] Error fetching comments:', e);
return [];
}
},
getSubscriptionStatus: async (userId, itemId) => {
if (!userId || !itemId) return false;
try {
const sub = await db`SELECT 1 FROM comment_subscriptions WHERE user_id = ${userId} AND item_id = ${itemId}`;
return sub.length > 0;
} catch (e) {
return false;
}
}
};

View File

@@ -42,6 +42,26 @@ export default (router, tpl) => {
});
}
// Preload comments for instant rendering (if logged in)
if (req.session) {
data.comments = await f0cklib.getComments(req.params.itemid);
// Also need subscription status? comments.js handles subscription toggle separately but initial state?
// API returns is_subscribed.
// Let's optimize later or just fetch simple comments list.
// Subscription status and is_locked/is_admin might be needed for comments.js to FULLY render without API call.
// But comments.js fetches API mainly for comments list. It also gets is_admin etc.
// If I provide comments list, comments.js skips fetch.
// It uses `this.isAdmin` from DOM. `this.isLocked` from DOM.
// `isSubscribed`? Not in DOM yet.
// I should add `data-is-subscribed` to DOM?
const sub = await f0cklib.getSubscriptionStatus(req.session.id, req.params.itemid);
data.isSubscribed = sub;
data.commentsJSON = Buffer.from(JSON.stringify(data.comments || [])).toString('base64');
} else {
data.comments = [];
data.commentsJSON = Buffer.from('[]').toString('base64');
}
// Inject session into data for the template
// We clone session to avoid unintended side effects or collisions
if (req.session) {

View File

@@ -29,18 +29,7 @@ export default (router, tpl) => {
const item = await db`SELECT is_comments_locked FROM items WHERE id = ${itemId}`;
const is_locked = item.length > 0 ? item[0].is_comments_locked : false;
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} AND c.is_deleted = false
ORDER BY COALESCE(c.is_pinned, false) DESC, c.created_at ${db.unsafe(sort === 'new' ? 'DESC' : 'ASC')}
`;
const comments = await f0cklib.getComments(itemId, sort);
let is_subscribed = false;
if (req.session) {

View File

@@ -105,6 +105,16 @@ export default (router, tpl) => {
if (mode === 'item') {
data.hidePagination = true;
if (req.session) {
data.comments = await f0cklib.getComments(req.params.itemid);
const sub = await f0cklib.getSubscriptionStatus(req.session.id, req.params.itemid);
data.isSubscribed = sub;
data.commentsJSON = Buffer.from(JSON.stringify(data.comments || [])).toString('base64');
} else {
data.comments = [];
data.isSubscribed = false;
data.commentsJSON = Buffer.from('[]').toString('base64');
}
}
return res.reply({ body: tpl.render(mode, data, req) });