From 01596c045a4637acf056f75cf87ffc0198d79899 Mon Sep 17 00:00:00 2001 From: Definisi Date: Tue, 14 Apr 2026 01:39:48 +0700 Subject: [PATCH] fix: Apply active filters to chapter count display (Closes #1793) (#1797) * fix: Apply active filters to chapter count display (Closes #1793) getChapterCount was always returning the total count regardless of the active filter. When filtering by unread/downloaded/bookmarked, the count shown in the novel header stayed the same as the total. Added an optional filter parameter to getChapterCount and pass the current settingsFilter from useNovel so the displayed count matches the filtered chapter list. * fix lint warning and add tests for filtered chapter count - Use settingsFilter instead of novelSettings.filter in useNovel to fix the react-hooks/exhaustive-deps warning - Add tests for getChapterCount with filter parameter - Update chapterFilterToSQL mock in test setup to actually apply filter conditions so filtered count tests work correctly --- src/database/queries/ChapterQueries.ts | 12 ++++- .../queries/__tests__/ChapterQueries.test.ts | 48 +++++++++++++++++++ src/database/queries/__tests__/setup.ts | 24 ++++++++-- src/hooks/persisted/useNovel.ts | 6 +-- 4 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/database/queries/ChapterQueries.ts b/src/database/queries/ChapterQueries.ts index 1bc04835..981ad26b 100644 --- a/src/database/queries/ChapterQueries.ts +++ b/src/database/queries/ChapterQueries.ts @@ -395,10 +395,18 @@ export const getPageChapters = async ( return query.all(); }; -export const getChapterCount = async (novelId: number, page: string = '1') => +export const getChapterCount = async ( + novelId: number, + page: string = '1', + filter?: ChapterFilterKey[], +) => await dbManager.$count( chapterSchema, - and(eq(chapterSchema.novelId, novelId), eq(chapterSchema.page, page)), + and( + eq(chapterSchema.novelId, novelId), + eq(chapterSchema.page, page), + chapterFilterToSQL(filter), + ), ); export const getPageChaptersBatched = async ( diff --git a/src/database/queries/__tests__/ChapterQueries.test.ts b/src/database/queries/__tests__/ChapterQueries.test.ts index 4777f451..005d4c00 100644 --- a/src/database/queries/__tests__/ChapterQueries.test.ts +++ b/src/database/queries/__tests__/ChapterQueries.test.ts @@ -712,6 +712,54 @@ describe('ChapterQueries', () => { expect(result).toBe(2); }); + + it('should return filtered count when filter is provided', async () => { + const testDb = getTestDb(); + const novelId = await insertTestNovel(testDb, { inLibrary: true }); + await insertTestChapter(testDb, novelId, { + page: '1', + unread: true, + }); + await insertTestChapter(testDb, novelId, { + page: '1', + unread: false, + }); + await insertTestChapter(testDb, novelId, { + page: '1', + unread: true, + }); + + const total = await getChapterCount(novelId, '1'); + expect(total).toBe(3); + + const unreadOnly = await getChapterCount(novelId, '1', ['not-read']); + expect(unreadOnly).toBe(2); + + const readOnly = await getChapterCount(novelId, '1', ['read']); + expect(readOnly).toBe(1); + }); + + it('should return total count when no filter is provided', async () => { + const testDb = getTestDb(); + const novelId = await insertTestNovel(testDb, { inLibrary: true }); + await insertTestChapter(testDb, novelId, { + page: '1', + unread: true, + }); + await insertTestChapter(testDb, novelId, { + page: '1', + unread: false, + }); + + const result = await getChapterCount(novelId, '1'); + expect(result).toBe(2); + + const noFilter = await getChapterCount(novelId, '1', undefined); + expect(noFilter).toBe(2); + + const emptyFilter = await getChapterCount(novelId, '1', []); + expect(emptyFilter).toBe(2); + }); }); describe('getPageChaptersBatched', () => { diff --git a/src/database/queries/__tests__/setup.ts b/src/database/queries/__tests__/setup.ts index f7b88c8d..4e114c11 100644 --- a/src/database/queries/__tests__/setup.ts +++ b/src/database/queries/__tests__/setup.ts @@ -115,10 +115,26 @@ jest.mock('expo-document-picker', () => ({ })); // Mock database utilities -jest.mock('@database/utils/parser', () => ({ - chapterFilterToSQL: jest.fn().mockReturnValue(undefined), - chapterOrderToSQL: jest.fn().mockReturnValue(undefined), -})); +jest.mock('@database/utils/parser', () => { + const { sql } = require('drizzle-orm'); + return { + chapterFilterToSQL: jest.fn().mockImplementation(filter => { + if (!filter || !filter.length) return undefined; + const map: Record = { + 'read': '`unread`=0', + 'not-read': '`unread`=1', + 'downloaded': 'isDownloaded=1', + 'not-downloaded': 'isDownloaded=0', + 'bookmarked': 'bookmark=1', + 'not-bookmarked': 'bookmark=0', + }; + const parts = filter.map((f: string) => map[f]).filter(Boolean); + if (!parts.length) return undefined; + return sql.raw(parts.join(' AND ')); + }), + chapterOrderToSQL: jest.fn().mockReturnValue(undefined), + }; +}); // Mock database constants jest.mock('@database/constants', () => ({ diff --git a/src/hooks/persisted/useNovel.ts b/src/hooks/persisted/useNovel.ts index ac46ef03..eee13ca6 100644 --- a/src/hooks/persisted/useNovel.ts +++ b/src/hooks/persisted/useNovel.ts @@ -238,7 +238,7 @@ export const useNovel = (novelOrPath: string | NovelInfo, pluginId: string) => { const config = [novel.id, settingsSort, settingsFilter, page] as const; - let chapterCount = await getChapterCount(novel.id, page); + let chapterCount = await getChapterCount(novel.id, page, settingsFilter); if (chapterCount) { try { @@ -259,7 +259,7 @@ export const useNovel = (novelOrPath: string | NovelInfo, pluginId: string) => { }); await insertChapters(novel.id, sourceChapters); newChapters = await _getPageChapters(...config); - chapterCount = await getChapterCount(novel.id, page); + chapterCount = await getChapterCount(novel.id, page, settingsFilter); } setBatchInformation({ @@ -271,7 +271,7 @@ export const useNovel = (novelOrPath: string | NovelInfo, pluginId: string) => { const unread = await _getFirstUnreadChapter( novel.id, - novelSettings.filter, + settingsFilter, page, ); setFirstUnreadChapter(unread ?? undefined);