fix: Preserve Default Category After Reordering (Closes #1922)

This commit is contained in:
Rajarshee Chatterjee
2026-07-28 13:36:37 +05:30
parent 0c3542836c
commit ee3a4f20bc
6 changed files with 43 additions and 27 deletions
+5
View File
@@ -28,3 +28,8 @@ export const CHAPTER_FILTER = {
export type ChapterFilterPositiveKey = keyof typeof CHAPTER_FILTER_POSITIVE;
export type ChapterFilterKey = keyof typeof CHAPTER_FILTER;
export type ChapterFilter = (typeof CHAPTER_FILTER)[ChapterFilterKey];
export const BUILT_IN_CATEGORY_IDS = {
default: 1,
local: 2,
} as const;
+2 -2
View File
@@ -186,10 +186,10 @@ export const updateCategoryOrderInDb = async (
}
await dbManager.write(async tx => {
for (const category of categories) {
for (const [index, category] of categories.entries()) {
await tx
.update(categorySchema)
.set({ sort: category.sort })
.set({ sort: index + 1 })
.where(eq(categorySchema.id, category.id))
.run();
}
+4 -3
View File
@@ -21,6 +21,7 @@ import {
import type { TransactionParameter } from '@database/manager/manager.d';
import { getLibraryDefaultCategoryId } from '@hooks/persisted/useSettings';
import NativeFile from '@modules/native-file';
import { BUILT_IN_CATEGORY_IDS } from '@database/constants';
const getCategoryForNewNovel = async (tx: TransactionParameter) => {
const preferredCategoryId = getLibraryDefaultCategoryId();
@@ -40,7 +41,7 @@ const getCategoryForNewNovel = async (tx: TransactionParameter) => {
return tx
.select({ id: categorySchema.id })
.from(categorySchema)
.where(eq(categorySchema.sort, 1))
.where(eq(categorySchema.id, BUILT_IN_CATEGORY_IDS.default))
.get();
};
@@ -174,7 +175,7 @@ export const switchNovelToLibraryQuery = async (
.insert(novelCategorySchema)
.values({
novelId: novel.id,
categoryId: 2,
categoryId: BUILT_IN_CATEGORY_IDS.local,
})
.onConflictDoNothing()
.run();
@@ -386,7 +387,7 @@ export const updateNovelCategories = async (
.where(
and(
inArray(novelCategorySchema.novelId, novelIds),
ne(novelCategorySchema.categoryId, 2),
ne(novelCategorySchema.categoryId, BUILT_IN_CATEGORY_IDS.local),
),
)
.run();
@@ -212,7 +212,7 @@ describe('CategoryQueries', () => {
});
describe('updateCategoryOrderInDb', () => {
it('should update sort order for multiple categories', async () => {
it('should persist categories using one-based display order', async () => {
const testDb = getTestDb();
const cat1 = await insertTestCategory(testDb, {
name: 'Cat 1',
@@ -231,8 +231,8 @@ describe('CategoryQueries', () => {
const categories = await getCategoriesFromDb();
const updated1 = categories.find(c => c.id === cat1);
const updated2 = categories.find(c => c.id === cat2);
expect(updated1?.sort).toBe(30);
expect(updated2?.sort).toBe(40);
expect(updated1?.sort).toBe(1);
expect(updated2?.sort).toBe(2);
});
it('should handle empty array', async () => {
@@ -13,7 +13,8 @@ import {
clearAllTables,
} from './testData';
import { categorySchema, novelCategorySchema } from '@database/schema';
import { eq, sql } from 'drizzle-orm';
import { eq } from 'drizzle-orm';
import { BUILT_IN_CATEGORY_IDS } from '@database/constants';
import {
getAllNovels,
@@ -178,7 +179,7 @@ describe('NovelQueries', () => {
expect(Boolean(novel?.inLibrary)).toBe(false);
});
it('should assign default category when adding to library', async () => {
it('should fall back to the built-in default category by ID', async () => {
const testDb = getTestDb();
const novelId = await insertTestNovel(testDb, {
inLibrary: false,
@@ -186,12 +187,16 @@ describe('NovelQueries', () => {
pluginId: 'test-plugin',
});
// Get default category (sort = 1)
const defaultCategory = await testDb.drizzleDb
.select()
.from(categorySchema)
.where(sql`${categorySchema.sort} = 1`)
.get();
await testDb.drizzleDb
.update(categorySchema)
.set({ sort: 0 })
.where(eq(categorySchema.id, BUILT_IN_CATEGORY_IDS.default))
.run();
await testDb.drizzleDb
.update(categorySchema)
.set({ sort: 1 })
.where(eq(categorySchema.id, BUILT_IN_CATEGORY_IDS.local))
.run();
await switchNovelToLibraryQuery('/test/novel', 'test-plugin');
@@ -202,10 +207,17 @@ describe('NovelQueries', () => {
.all();
expect(associations.length).toBeGreaterThan(0);
expect(
associations.some(a => a.categoryId === (defaultCategory?.id ?? -1)),
).toBe(!!defaultCategory);
associations.some(
association =>
association.categoryId === BUILT_IN_CATEGORY_IDS.default,
),
).toBe(true);
expect(
associations.some(
association => association.categoryId === BUILT_IN_CATEGORY_IDS.local,
),
).toBe(false);
});
it('should assign the user-selected default category', async () => {
@@ -257,7 +269,10 @@ describe('NovelQueries', () => {
.all();
expect(
associations.some(association => association.categoryId === 1),
associations.some(
association =>
association.categoryId === BUILT_IN_CATEGORY_IDS.default,
),
).toBe(true);
});
});
+2 -7
View File
@@ -51,13 +51,8 @@ const CategoriesScreen = () => {
return;
}
const updatedOrderCategories = data.map((category, index) => ({
...category,
sort: index,
}));
setCategories(updatedOrderCategories);
updateCategoryOrderInDb(updatedOrderCategories);
setCategories(data);
updateCategoryOrderInDb(data);
};
const renderItem = ({