From 508d6993ee6226298aa2526831581be51dd5f42b Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 15 Jun 2026 11:53:05 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Feed-Pagination=20=E2=80=93=20erledigte?= =?UTF-8?q?=20und=20vom=20Client=20gelieferte=20Pairs=20ausschlie=C3=9Fen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET /auth/feed schließt jetzt Pairs aus user_pair_progress (cross-session) sowie per ?exclude= übergebene, bereits geladene Pairs (In-Session) aus. Leere Antwort signalisiert dem Client: keine weiteren Karten. Co-Authored-By: Claude Opus 4.8 --- src/routes/feed.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/routes/feed.js b/src/routes/feed.js index b82ce9d..9230502 100644 --- a/src/routes/feed.js +++ b/src/routes/feed.js @@ -39,12 +39,20 @@ function collectIds(lists, filterType) { router.get('/', requireJwt, async (req, res, next) => { try { - const lang = ['de', 'en', 'sv'].includes(req.query.lang) ? req.query.lang : 'de'; - const limit = Math.min(parseInt(req.query.limit) || 20, 100); + const lang = ['de', 'en', 'sv'].includes(req.query.lang) ? req.query.lang : 'de'; + const limit = Math.min(parseInt(req.query.limit) || 20, 100); + const userId = req.user.userId; + // Vom Client schon geladene Pairs (In-Session-Dedupe) – nur gültige UUIDs übernehmen. + const exclude = String(req.query.exclude || '') + .split(',') + .map(s => s.trim()) + .filter(s => /^[0-9a-f-]{36}$/i.test(s)); // 1. Random pairs — only fully ready content: // pair published + linked question/statements published + a published picture exists. // (Audio coverage is additionally enforced in Phase 2.) + // Pagination: bereits abgeschlossene (user_pair_progress) und vom Client + // geladene Pairs werden ausgeschlossen; leere Antwort = keine weiteren Karten. const pairsRes = await query( `SELECT p.id, p.answer_type, p.status, p.difficulty_level, p.question_id, p.positive_statement_id, p.negative_statement_id @@ -61,9 +69,13 @@ router.get('/', requireJwt, async (req, res, next) => { JOIN object_pictures pic ON pic.object_id = op.object_id JOIN pictures pp ON pp.id = pic.picture_id WHERE op.pair_id = p.id AND pp.status = 'published') + AND NOT EXISTS ( + SELECT 1 FROM user_pair_progress upp + WHERE upp.pair_id = p.id AND upp.user_id = $2) + AND p.id <> ALL($3::uuid[]) ORDER BY random() LIMIT $1`, - [limit] + [limit, userId, exclude] ); if (!pairsRes.rows.length) return res.json([]); const pairs = pairsRes.rows;