import f0cklib from "../routeinc/f0cklib.mjs"; import url from "url"; export default (router, tpl) => { router.get(/\/ajax\/item\/(?\d+)/, async (req, res) => { let query = {}; if (typeof req.url === 'string') { const parsedUrl = url.parse(req.url, true); query = parsedUrl.query; } else { // flummpress uses req.url.qs for query string parameters query = req.url.qs || {}; } let contextUrl = `/${req.params.itemid}`; if (query.tag) contextUrl = `/tag/${encodeURIComponent(query.tag)}/${req.params.itemid}`; if (query.user) { contextUrl = query.fav === 'true' ? `/user/${encodeURIComponent(query.user)}/favs/${req.params.itemid}` : `/user/${encodeURIComponent(query.user)}/${req.params.itemid}`; } console.log('[AJAX DEBUG] Params:', { itemid: req.params.itemid, user: query.user, fav: query.fav, contextUrl }); const data = await f0cklib.getf0ck({ itemid: req.params.itemid, mode: req.session.mode, session: !!req.session, url: contextUrl, user: query.user, tag: query.tag, mime: query.mime, fav: query.fav === 'true' }); console.log('[AJAX DEBUG] getf0ck result:', { success: data.success, message: data.message }); if (!data.success) { return res.reply({ code: 404, body: "

404 - Not f0cked

" }); } // 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.isSubscribed = false; 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) { data.session = { ...req.session }; // data.user comes from f0cklib (uploader). req.session.user is logged-in user string. // Templates use session.user for matching favorites. We must preserve it. // if (data.session.user) delete data.session.user; // REMOVED THIS } else { data.session = false; } // Inject missing variables normally provided by req or middleware data.url = { pathname: contextUrl }; // Template expects url.pathname data.fullscreen = req.cookies.fullscreen || 0; // Index.mjs uses req.cookies.fullscreen data.hidePagination = true; // Render both the item content and the pagination const itemHtml = tpl.render('ajax-item', data); const paginationHtml = tpl.render('snippets/pagination', data); // Return JSON response return res.reply({ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ html: itemHtml, pagination: paginationHtml }) }); }); // Infinite scroll endpoint for index thumbnails router.get(/\/ajax\/items/, async (req, res) => { let query = {}; if (typeof req.url === 'string') { const parsedUrl = url.parse(req.url, true); query = parsedUrl.query; } else { query = req.url.qs || {}; } const page = parseInt(query.page) || 1; const data = await f0cklib.getf0cks({ page: page, tag: query.tag || null, user: query.user || null, mime: query.mime || null, mode: req.session.mode, session: !!req.session, fav: false }); if (!data.success) { return res.reply({ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ success: false, html: '', hasMore: false }) }); } // Render just the thumbnail items const itemsHtml = tpl.render('snippets/items-grid', { items: data.items, link: data.link }); // Render pagination const paginationHtml = tpl.render('snippets/pagination', { pagination: data.pagination, link: data.link }); const hasMore = data.pagination.next !== null; return res.reply({ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ success: true, html: itemsHtml, pagination: paginationHtml, hasMore: hasMore, nextPage: data.pagination.next, currentPage: data.pagination.page }) }); }); return router; };