diff --git a/ui/src/lib/server/pocketbase.ts b/ui/src/lib/server/pocketbase.ts index 1c26f63..85a1dbe 100644 --- a/ui/src/lib/server/pocketbase.ts +++ b/ui/src/lib/server/pocketbase.ts @@ -299,7 +299,8 @@ export async function invalidateBooksCache(): Promise { await Promise.all([ cache.invalidate(BOOKS_CACHE_KEY), cache.invalidate(HOME_STATS_CACHE_KEY), - cache.invalidatePattern('books:recent:*') + cache.invalidatePattern('books:recent:*'), + cache.invalidatePattern('books:recently-updated:*') ]); } @@ -312,10 +313,46 @@ export async function recentlyAddedBooks(limit = 6): Promise { const cached = await cache.get(key); if (cached) return cached; const books = await listN('books', limit, '', '-meta_updated'); - await cache.set(key, books, 5 * 60); // 5 minutes + await cache.set(key, books, 5 * 60); return books; } +/** + * Books with the most recently added chapters, ordered by chapter insertion time. + * Queries chapters_idx sorted by -created, deduplicates by slug, then loads books. + * This correctly reflects actual chapter activity, unlike meta_updated on books. + */ +export async function recentlyUpdatedBooks(limit = 8): Promise { + const key = `books:recently-updated:${limit}`; + const cached = await cache.get(key); + if (cached) return cached; + + // Fetch enough recent chapter rows to find `limit` distinct books + const rows = await listN<{ slug: string; created: string }>( + 'chapters_idx', limit * 25, '', '-created' + ); + + const seen = new Set(); + const slugs: string[] = []; + for (const row of rows) { + if (!seen.has(row.slug)) { + seen.add(row.slug); + slugs.push(row.slug); + if (slugs.length >= limit) break; + } + } + + if (!slugs.length) return []; + + const books = await getBooksBySlugs(new Set(slugs)); + // Restore recency order (getBooksBySlugs returns in title sort order) + const bookMap = new Map(books.map((b) => [b.slug, b])); + const ordered = slugs.flatMap((s) => (bookMap.has(s) ? [bookMap.get(s)!] : [])); + + await cache.set(key, ordered, 5 * 60); + return ordered; +} + export interface HomeStats { totalBooks: number; totalChapters: number; diff --git a/ui/src/routes/+page.server.ts b/ui/src/routes/+page.server.ts index 2e440e1..99aa8c3 100644 --- a/ui/src/routes/+page.server.ts +++ b/ui/src/routes/+page.server.ts @@ -1,7 +1,7 @@ import type { PageServerLoad } from './$types'; import { getBooksBySlugs, - recentlyAddedBooks, + recentlyUpdatedBooks, allProgress, getHomeStats, getSubscriptionFeed @@ -19,7 +19,7 @@ export const load: PageServerLoad = async ({ locals }) => { try { [recentBooks, progressList, stats] = await Promise.all([ - recentlyAddedBooks(8), + recentlyUpdatedBooks(8), allProgress(locals.sessionId, locals.user?.id), getHomeStats() ]); diff --git a/ui/src/routes/+page.svelte b/ui/src/routes/+page.svelte index 08d39df..e75be98 100644 --- a/ui/src/routes/+page.svelte +++ b/ui/src/routes/+page.svelte @@ -1,9 +1,47 @@