diff --git a/public/s/js/comments.js b/public/s/js/comments.js index cecf51a..ef2f296 100644 --- a/public/s/js/comments.js +++ b/public/s/js/comments.js @@ -57,6 +57,38 @@ class CommentSystem { return; } + // Check for server-side preloaded comments + // Check for server-side preloaded comments (Script Tag Method) + const dataEl = document.getElementById('initial-comments'); + if (dataEl) { + try { + // Decode Base64 for safe template transfer + const raw = dataEl.textContent.trim(); + const json = atob(raw); + const comments = JSON.parse(json); + + const subEl = document.getElementById('initial-subscription'); + // Handle boolean text content + const isSubscribed = subEl && (subEl.textContent.trim() === 'true'); + + // Consume + dataEl.remove(); + if (subEl) subEl.remove(); + + this.render(comments, this.user, isSubscribed); + + if (scrollToId) { + this.scrollToComment(scrollToId); + } else if (window.location.hash && window.location.hash.startsWith('#c')) { + const hashId = window.location.hash.substring(2); + this.scrollToComment(hashId); + } + return; + } catch (e) { + console.error("SSR comments parse error", e); + } + } + // Render skeleton (Result: Layout visible immediately) if (!scrollToId) { this.render([], this.user, false); diff --git a/src/inc/routeinc/f0cklib.mjs b/src/inc/routeinc/f0cklib.mjs index 9f717ed..f1d7bfa 100644 --- a/src/inc/routeinc/f0cklib.mjs +++ b/src/inc/routeinc/f0cklib.mjs @@ -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; + } } }; diff --git a/src/inc/routes/ajax.mjs b/src/inc/routes/ajax.mjs index 7448588..a2e89ac 100644 --- a/src/inc/routes/ajax.mjs +++ b/src/inc/routes/ajax.mjs @@ -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) { diff --git a/src/inc/routes/comments.mjs b/src/inc/routes/comments.mjs index 426106f..b91cf6a 100644 --- a/src/inc/routes/comments.mjs +++ b/src/inc/routes/comments.mjs @@ -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) { diff --git a/src/inc/routes/index.mjs b/src/inc/routes/index.mjs index 545b77e..83e214a 100644 --- a/src/inc/routes/index.mjs +++ b/src/inc/routes/index.mjs @@ -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) }); diff --git a/views/item-partial.html b/views/item-partial.html index 10e4d9f..93f3d23 100644 --- a/views/item-partial.html +++ b/views/item-partial.html @@ -126,4 +126,18 @@
\ No newline at end of file + @if(session.admin)data-is-admin="true" @endif @endif @if(item.is_comments_locked)data-is-locked="true" @endif> +
+

Comments @if(item.is_comments_locked)🔒@endif

+
+ Loading... +
+
+ @if(session && session.user && !item.is_comments_locked) +
+ +
+ @endif + + + \ No newline at end of file