From fcd2945374b64cb401cecf37b7c30b17f97a62ea Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 19:57:27 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`DB=5FMa?= =?UTF-8?q?nager`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @CD-Z. * https://github.com/CD-Z/lnreader/pull/23#issuecomment-3927901182 The following files were modified: * `src/components/NovelCover.tsx` * `src/database/manager/manager.ts` * `src/database/queries/__tests__/setup.ts` * `src/database/queries/__tests__/testData.ts` * `src/database/queries/__tests__/testDb.ts` * `src/database/queries/__tests__/testDbManager.ts` * `src/database/utils/parser.ts` * `src/hooks/persisted/useNovel.ts` * `src/screens/novel/NovelContext.tsx` * `src/screens/reader/hooks/useChapter.ts` --- src/components/NovelCover.tsx | 19 +++++++- src/database/manager/manager.ts | 17 ++++++- src/database/queries/__tests__/setup.ts | 18 +++++-- src/database/queries/__tests__/testData.ts | 48 +++++++++++++++---- src/database/queries/__tests__/testDb.ts | 19 +++++--- .../queries/__tests__/testDbManager.ts | 8 +++- src/database/utils/parser.ts | 14 +++++- src/hooks/persisted/useNovel.ts | 9 +++- src/screens/novel/NovelContext.tsx | 9 +++- src/screens/reader/hooks/useChapter.ts | 13 ++++- 10 files changed, 146 insertions(+), 28 deletions(-) diff --git a/src/components/NovelCover.tsx b/src/components/NovelCover.tsx index eebb9cb10f..c37935868b 100644 --- a/src/components/NovelCover.tsx +++ b/src/components/NovelCover.tsx @@ -73,6 +73,23 @@ function isFromDB( return 'chaptersDownloaded' in item; } +/** + * Render a novel cover (grid or list) with badges, title, image, and selection handling. + * + * @param item - Novel item data (cover, name, and optional DB-specific chapter counts). + * @param onPress - Callback invoked when the cover is pressed (used when not in selection mode). + * @param libraryStatus - Whether the novel is present in the user's library (shows InLibrary badge and alters cover opacity). + * @param theme - Theme colors used for badges, ripples, and title styling. + * @param isSelected - Whether the novel is currently selected (affects visual highlighting). + * @param addSkeletonLoading - When true and `item.completeRow` is present, render skeleton loading for that row; otherwise render nothing for that row. + * @param inActivity - When true, show a small activity badge on the cover. + * @param onLongPress - Callback invoked to trigger selection behavior; receives the novel item. + * @param hasSelection - Optional explicit flag to enable selection mode; if omitted, selection mode is inferred from `selectedNovelIds`. + * @param globalSearch - When true, use the global-search layout and sizing (three-column grid behaviour). + * @param selectedNovelIds - Optional array of selected novel IDs used to infer selection mode when `hasSelection` is not provided. + * @param imageRequestInit - Optional image request init (headers, etc.); a User-Agent header is injected if none provided. + * @returns A React element that displays the novel cover in the appropriate layout (grid or list) or a skeleton loading component when applicable. + */ function NovelCover< TNovel extends CoverItemLibrary | CoverItemPlugin | CoverItemDB, >({ @@ -462,4 +479,4 @@ const styles = StyleSheet.create({ paddingHorizontal: 4, paddingTop: 2, }, -}); +}); \ No newline at end of file diff --git a/src/database/manager/manager.ts b/src/database/manager/manager.ts index 1d8667ad07..4506746550 100644 --- a/src/database/manager/manager.ts +++ b/src/database/manager/manager.ts @@ -20,6 +20,12 @@ interface ExecutableSelect { let _dbManager: DbManager; +/** + * Produces a SQL expression that casts the given value to INTEGER. + * + * @param value - A literal value, string, or SQL column/expression to cast to integer + * @returns A SQL fragment representing `CAST(value AS INTEGER)` + */ export function castInt(value: number | string | AnyColumn) { return sql`CAST(${value} AS INTEGER)`; } @@ -132,6 +138,15 @@ export const createDbManager = (dbInstance: DrizzleDb) => { type TableNames = GetSelectTableName; type FireOn = Array<{ table: TableNames; ids?: number[] }>; +/** + * Subscribes to a query and returns its current result set, updating when the database signals relevant changes. + * + * Initializes state with a synchronous execution of `query`, then registers a reactive subscription that updates the returned rows when records for the specified tables/IDs change. + * + * @param query - An executable select query whose results will be observed. + * @param fireOn - Array of triggers specifying table names and optional ID lists that should cause the query to refresh when modified. + * @returns The current rows produced by `query.all()`; updates automatically when matching database changes occur. + */ export function useLiveQuery( query: T, fireOn: FireOn, @@ -160,4 +175,4 @@ export function useLiveQuery( }, [sqlString, paramsKey, fireOnKey]); return data; -} +} \ No newline at end of file diff --git a/src/database/queries/__tests__/setup.ts b/src/database/queries/__tests__/setup.ts index f7b88c8d06..ac06c0ff23 100644 --- a/src/database/queries/__tests__/setup.ts +++ b/src/database/queries/__tests__/setup.ts @@ -15,8 +15,11 @@ import { createTestDb, cleanupTestDb, type TestDb } from './testDb'; let mockTestDbInstance: TestDb | null = null; /** - * Sets up the test database - * This should be called in beforeEach of test files + * Initialize and return the in-memory test database for queries. + * + * If a test database is already initialized, it is first cleaned up and replaced. + * + * @returns The initialized test database instance */ export function setupTestDatabase(): TestDb { if (mockTestDbInstance) { @@ -27,7 +30,10 @@ export function setupTestDatabase(): TestDb { } /** - * Gets the current test database instance + * Retrieves the active test database instance for the current test run. + * + * @returns The initialized TestDb instance. + * @throws Error if the test database has not been initialized; call setupTestDatabase() first. */ export function getTestDb(): TestDb { if (!mockTestDbInstance) { @@ -39,7 +45,9 @@ export function getTestDb(): TestDb { } /** - * Cleans up the test database + * Dispose of the current in-memory test database and clear its module reference. + * + * If a test database has been initialized, calls the cleanup routine and sets the internal instance to `null`. */ export function teardownTestDatabase() { if (mockTestDbInstance) { @@ -152,4 +160,4 @@ jest.setTimeout(10000); // The database is automatically cleaned up in afterAll afterAll(() => { teardownTestDatabase(); -}); +}); \ No newline at end of file diff --git a/src/database/queries/__tests__/testData.ts b/src/database/queries/__tests__/testData.ts index 4df447e076..d62b580ef6 100644 --- a/src/database/queries/__tests__/testData.ts +++ b/src/database/queries/__tests__/testData.ts @@ -17,7 +17,9 @@ import { } from '@database/schema'; /** - * Clears all tables in the test database + * Remove all rows from test tables used by the application. + * + * Specifically deletes entries from NovelCategory, Chapter, Novel, and Repository, and deletes Category rows whose `id` is greater than 2 (preserving `id` 1 and 2). */ export function clearAllTables(testDb: TestDb) { const { sqlite } = testDb; @@ -31,7 +33,11 @@ export function clearAllTables(testDb: TestDb) { } /** - * Inserts a test novel into the database + * Create and insert a test novel record into the database. + * + * @param testDb - Test database context used for the insertion + * @param data - Optional fields to override the default novel properties + * @returns The id of the inserted novel */ export async function insertTestNovel( testDb: TestDb, @@ -70,7 +76,11 @@ export async function insertTestNovel( } /** - * Inserts a test chapter into the database + * Create and insert a chapter record linked to a novel for tests. + * + * @param novelId - The id of the novel to associate the chapter with + * @param data - Optional partial chapter fields to override default test values + * @returns The inserted chapter's id */ export async function insertTestChapter( testDb: TestDb, @@ -106,7 +116,10 @@ export async function insertTestChapter( } /** - * Inserts a test category into the database + * Inserts a category row for tests, using sensible defaults for missing fields. + * + * @param data - Optional overrides for the category fields; if `name` is omitted a timestamped default is used + * @returns The id of the newly inserted category */ export async function insertTestCategory( testDb: TestDb, @@ -128,7 +141,11 @@ export async function insertTestCategory( } /** - * Inserts a test repository into the database + * Create and insert a repository record for tests. + * + * If `data.url` is not provided, a unique test URL is generated. + * + * @returns The inserted repository's id */ export async function insertTestRepository( testDb: TestDb, @@ -149,7 +166,9 @@ export async function insertTestRepository( } /** - * Inserts a novel-category association + * Creates an association between a novel and a category in the test database. + * + * @returns The id of the created novel-category association */ export async function insertTestNovelCategory( testDb: TestDb, @@ -172,7 +191,11 @@ export async function insertTestNovelCategory( } /** - * Inserts a novel with optional chapters + * Create a test novel and optionally add chapters linked to it. + * + * @param novelData - Partial fields to override on the created novel + * @param chapters - Array of partial chapter records to insert for the novel; each entry becomes a chapter linked to the created novel + * @returns An object containing the inserted novel's `novelId` and an array of inserted `chapterIds` */ export async function insertTestNovelWithChapters( testDb: TestDb, @@ -201,6 +224,15 @@ export interface TestFixtures { novelCategories?: Array<{ novelId: number; categoryId: number }>; } +/** + * Inserts provided test fixtures into the test database and returns the created record IDs. + * + * Processes fixtures in this order when present: novels, chapters, categories, repositories, and novel-category associations. + * + * @param testDb - Test database context used for performing inserts + * @param fixtures - Collections of fixture objects to insert; fields can override default test values + * @returns An object containing arrays of inserted IDs: `novelIds`, `chapterIds`, `categoryIds`, and `repositoryIds` + */ export async function seedTestData( testDb: TestDb, fixtures: TestFixtures, @@ -256,4 +288,4 @@ export async function seedTestData( } return { novelIds, chapterIds, categoryIds, repositoryIds }; -} +} \ No newline at end of file diff --git a/src/database/queries/__tests__/testDb.ts b/src/database/queries/__tests__/testDb.ts index cf70e2efe3..3e584aa443 100644 --- a/src/database/queries/__tests__/testDb.ts +++ b/src/database/queries/__tests__/testDb.ts @@ -79,8 +79,11 @@ const MIGRATION_STATEMENTS = [ ]; /** - * Creates a fresh in-memory SQLite database with schema and migrations - * @returns Database instance and dbManager + * Create a fresh in-memory SQLite database preloaded with schema, migrations, triggers, and default categories for use in tests. + * + * The database is configured with testing-friendly PRAGMAs, migration SQL is applied, required tables are verified (including `Novel`), production-like triggers are created, and default Category rows are seeded. + * + * @returns An object with `sqlite` (the better-sqlite3 Database), `drizzleDb` (the Drizzle ORM instance bound to the database), and `dbManager` (a test-friendly database manager) */ export function createTestDb() { // Create in-memory database @@ -149,15 +152,19 @@ export function createTestDb() { } /** - * Closes and cleans up a test database + * Close the underlying SQLite connection for a test database. + * + * @param testDb - The test database object returned by `createTestDb`; its underlying SQLite connection will be closed */ export function cleanupTestDb(testDb: ReturnType) { testDb.sqlite.close(); } /** - * Gets a dbManager instance for a test database - * Convenience function for tests + * Retrieve the dbManager from a test database instance. + * + * @param testDb - The object returned by `createTestDb` + * @returns The `dbManager` associated with `testDb` */ export function getTestDbManager(testDb: ReturnType) { return testDb.dbManager; @@ -166,4 +173,4 @@ export function getTestDbManager(testDb: ReturnType) { /** * Type export for test database */ -export type TestDb = ReturnType; +export type TestDb = ReturnType; \ No newline at end of file diff --git a/src/database/queries/__tests__/testDbManager.ts b/src/database/queries/__tests__/testDbManager.ts index 1670509db9..3c4a8a1cd7 100644 --- a/src/database/queries/__tests__/testDbManager.ts +++ b/src/database/queries/__tests__/testDbManager.ts @@ -53,7 +53,11 @@ const wrapBuilder = (builder: T): T => { }; /** - * Creates a test-compatible dbManager that works with better-sqlite3 + * Create a test-compatible IDbManager that adapts a Drizzle ORM database and a better-sqlite3 Database. + * + * @param drizzleDb - The Drizzle ORM database instance to delegate query builders and async operations to + * @param sqlite - The better-sqlite3 Database instance used for synchronous test helpers + * @returns An IDbManager that delegates to `drizzleDb` and provides test-friendly methods (e.g., `getSync`, `allSync`, `batch`, `write`, `transaction`) */ export function createTestDbManager( drizzleDb: DrizzleDb, @@ -131,4 +135,4 @@ export function createTestDbManager( }; return dbManager; -} +} \ No newline at end of file diff --git a/src/database/utils/parser.ts b/src/database/utils/parser.ts index 4f27df2cf7..ef623aeb62 100644 --- a/src/database/utils/parser.ts +++ b/src/database/utils/parser.ts @@ -6,11 +6,23 @@ import { } from '@database/constants'; import { SQL, sql } from 'drizzle-orm'; +/** + * Convert a chapter order key into a raw SQL ordering fragment. + * + * @param order - Ordering key that selects which chapter sort expression to use + * @returns A raw SQL fragment representing the corresponding ORDER BY expression; falls back to the default position-ascending expression if the key is not found + */ export function chapterOrderToSQL(order: ChapterOrderKey) { const o = CHAPTER_ORDER[order] ?? CHAPTER_ORDER.positionAsc; return sql.raw(o); } +/** + * Builds an SQL condition fragment from an array of chapter filter keys. + * + * @param filter - Optional array of chapter filter keys to combine with `AND`; if omitted or empty, no filtering is applied + * @returns An `SQL` fragment representing the combined `WHERE`-style condition for the provided filters; when `filter` is undefined or empty, returns an expression that evaluates to `true` + */ export function chapterFilterToSQL(filter?: ChapterFilterKey[]) { if (!filter || !filter.length) return sql.raw('true'); let filters: SQL | undefined; @@ -22,4 +34,4 @@ export function chapterFilterToSQL(filter?: ChapterFilterKey[]) { } }); return filters ?? sql.raw('true'); -} +} \ No newline at end of file diff --git a/src/hooks/persisted/useNovel.ts b/src/hooks/persisted/useNovel.ts index 94a141110e..07c7274422 100644 --- a/src/hooks/persisted/useNovel.ts +++ b/src/hooks/persisted/useNovel.ts @@ -114,7 +114,12 @@ export const useNovel = (novelOrPath: string | NovelInfo, pluginId: string) => { ); // #endregion - // #region setters + /** + * Produce the list of page identifiers for a novel, using totalPages if available or custom pages otherwise. + * + * @param tmpNovel - Novel metadata used to determine available pages + * @returns An array of page numbers as strings; always contains at least `'1'` + */ async function calculatePages(tmpNovel: NovelInfo) { let tmpPages: string[]; @@ -687,4 +692,4 @@ export const deleteCachedNovels = async () => { } _deleteCachedNovels(); }; -// #endregion +// #endregion \ No newline at end of file diff --git a/src/screens/novel/NovelContext.tsx b/src/screens/novel/NovelContext.tsx index 2db08342a8..b530b6f29b 100644 --- a/src/screens/novel/NovelContext.tsx +++ b/src/screens/novel/NovelContext.tsx @@ -16,6 +16,13 @@ const defaultValue = {} as NovelContextType; const NovelContext = createContext(defaultValue); +/** + * Provides NovelContext to descendants by combining novel hook data with safe-area and orientation-derived layout metrics and a chapter text cache. + * + * @param children - Element subtree that will receive the context + * @param route - Navigation route for 'Novel' or 'Chapter'; used to derive the novel identifier (either `path` or a `NovelInfo` object) and `pluginId` + * @returns The NovelContext provider wrapping `children` + */ export function NovelContextProvider({ children, @@ -70,4 +77,4 @@ export function NovelContextProvider({ export const useNovelContext = () => { const context = useContext(NovelContext); return context; -}; +}; \ No newline at end of file diff --git a/src/screens/reader/hooks/useChapter.ts b/src/screens/reader/hooks/useChapter.ts index 1ac5716e10..0af2c1c7eb 100644 --- a/src/screens/reader/hooks/useChapter.ts +++ b/src/screens/reader/hooks/useChapter.ts @@ -38,6 +38,17 @@ import { useNovelContext } from '@screens/novel/NovelContext'; const emmiter = new NativeEventEmitter(NativeVolumeButtonListener); +/** + * Manages chapter loading, navigation, UI visibility, auto-scrolling, and reading progress for a reader WebView. + * + * Handles loading and caching chapter text, prefetching adjacent chapters/pages, volume-button and auto-scroll controls, + * tracking/marking progress, history updates, and immersive mode toggling. + * + * @param webViewRef - Ref to the WebView used to render and control the chapter content + * @param initialChapter - The initial ChapterInfo to display + * @param novel - Metadata for the current novel (used for fetching, caching, and pagination) + * @returns An object exposing reader state (hidden, chapter, nextChapter, prevChapter, error, loading, chapterText), setters (setHidden, setChapter, setLoading), and actions (saveProgress, hideHeader, navigateChapter, refetch, getChapter) + */ export default function useChapter( webViewRef: RefObject, initialChapter: ChapterInfo, @@ -377,4 +388,4 @@ export default function useChapter( getChapter, ], ); -} +} \ No newline at end of file