diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt index 8788739d..71e1f9dd 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt @@ -12,6 +12,7 @@ import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger import org.jetbrains.exposed.sql.addLogger +import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.graphql.types.MangaNodeList @@ -42,10 +43,23 @@ class MangaForCategoryDataLoader : KotlinDataLoader { future { transaction { addLogger(Slf4jSqlDebugLogger) - val itemsByRef = CategoryMangaTable.innerJoin(MangaTable).select { CategoryMangaTable.category inList ids } + val itemsByRef = if (ids.contains(0)) { + MangaTable + .leftJoin(CategoryMangaTable) + .select { MangaTable.inLibrary eq true } + .andWhere { CategoryMangaTable.manga.isNull() } + .map { MangaType(it) } + .let { + mapOf(0 to it) + } + } else { + emptyMap() + } + CategoryMangaTable.innerJoin(MangaTable) + .select { CategoryMangaTable.category inList ids } .map { Pair(it[CategoryMangaTable.category].value, MangaType(it)) } .groupBy { it.first } .mapValues { it.value.map { pair -> pair.second } } + ids.map { (itemsByRef[it] ?: emptyList()).toNodeList() } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt index 6e6ac649..86c9723c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt @@ -10,6 +10,7 @@ package suwayomi.tachidesk.manga.impl import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.insertAndGetId @@ -19,7 +20,6 @@ import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass -import suwayomi.tachidesk.manga.model.dataclass.IncludeInUpdate import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryMetaTable import suwayomi.tachidesk.manga.model.table.CategoryTable @@ -54,7 +54,7 @@ object Category { transaction { CategoryTable.update({ CategoryTable.id eq categoryId }) { if (name != null && !name.equals(DEFAULT_CATEGORY_NAME, ignoreCase = true)) it[CategoryTable.name] = name - if (isDefault != null) it[CategoryTable.isDefault] = isDefault + if (isDefault != null && categoryId != DEFAULT_CATEGORY_ID) it[CategoryTable.isDefault] = isDefault if (includeInUpdate != null) it[CategoryTable.includeInUpdate] = includeInUpdate } } @@ -64,6 +64,7 @@ object Category { * Move the category from order number `from` to `to` */ fun reorderCategory(from: Int, to: Int) { + if (from == 0 || to == 0) return transaction { val categories = CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).toMutableList() categories.add(to - 1, categories.removeAt(from - 1)) @@ -76,6 +77,7 @@ object Category { } fun removeCategory(categoryId: Int) { + if (categoryId == DEFAULT_CATEGORY_ID) return transaction { CategoryMangaTable.select { CategoryMangaTable.category eq categoryId }.forEach { removeMangaFromCategory(it[CategoryMangaTable.manga].value, categoryId) @@ -97,24 +99,32 @@ object Category { } } + private fun needsDefaultCategory() = transaction { + MangaTable + .leftJoin(CategoryMangaTable) + .select { MangaTable.inLibrary eq true } + .andWhere { CategoryMangaTable.manga.isNull() } + .empty() + .not() + } + const val DEFAULT_CATEGORY_ID = 0 const val DEFAULT_CATEGORY_NAME = "Default" - private fun addDefaultIfNecessary(categories: List): List { - val defaultCategorySize = MangaTable.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }.count().toInt() - return if (defaultCategorySize > 0) { - listOf(CategoryDataClass(DEFAULT_CATEGORY_ID, 0, DEFAULT_CATEGORY_NAME, true, defaultCategorySize, IncludeInUpdate.UNSET)) + categories - } else { - categories - } - } fun getCategoryList(): List { return transaction { - val categories = CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).map { - CategoryTable.toDataClass(it) - } - - addDefaultIfNecessary(categories) + CategoryTable.selectAll() + .orderBy(CategoryTable.order to SortOrder.ASC) + .let { + if (needsDefaultCategory()) { + it + } else { + it.andWhere { CategoryTable.id neq DEFAULT_CATEGORY_ID } + } + } + .map { + CategoryTable.toDataClass(it) + } } } @@ -128,8 +138,15 @@ object Category { fun getCategorySize(categoryId: Int): Int { return transaction { - CategoryMangaTable.select { - CategoryMangaTable.category eq categoryId + if (categoryId == DEFAULT_CATEGORY_ID) { + MangaTable + .leftJoin(CategoryMangaTable) + .select { MangaTable.inLibrary eq true } + .andWhere { CategoryMangaTable.manga.isNull() } + } else { + CategoryMangaTable.select { + CategoryMangaTable.category eq categoryId + } }.count().toInt() } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt index b571c53d..28bc208d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt @@ -33,6 +33,7 @@ import suwayomi.tachidesk.manga.model.table.toDataClass object CategoryManga { fun addMangaToCategory(mangaId: Int, categoryId: Int) { + if (categoryId == DEFAULT_CATEGORY_ID) return fun notAlreadyInCategory() = CategoryMangaTable.select { (CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId) }.isEmpty() transaction { @@ -50,6 +51,7 @@ object CategoryManga { } fun removeMangaFromCategory(mangaId: Int, categoryId: Int) { + if (categoryId == DEFAULT_CATEGORY_ID) return transaction { CategoryMangaTable.deleteWhere { (CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId) } if (CategoryMangaTable.select { CategoryMangaTable.manga eq mangaId }.count() == 0L) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt index fb4ca318..9da580cc 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt @@ -7,6 +7,7 @@ package suwayomi.tachidesk.manga.impl * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction @@ -22,7 +23,9 @@ object Library { val manga = getManga(mangaId) if (!manga.inLibrary) { transaction { - val defaultCategories = CategoryTable.select { CategoryTable.isDefault eq true }.toList() + val defaultCategories = CategoryTable.select { + (CategoryTable.isDefault eq true) and (CategoryTable.id neq Category.DEFAULT_CATEGORY_ID) + }.toList() val existingCategories = CategoryMangaTable.select { CategoryMangaTable.manga eq mangaId }.toList() MangaTable.update({ MangaTable.id eq manga.id }) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0027_AddDefaultCategory.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0027_AddDefaultCategory.kt new file mode 100644 index 00000000..5954997c --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0027_AddDefaultCategory.kt @@ -0,0 +1,17 @@ +package suwayomi.tachidesk.server.database.migration + +import de.neonew.exposed.migrations.helpers.SQLMigration + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +@Suppress("ClassName", "unused") +class M0027_AddDefaultCategory : SQLMigration() { + override val sql: String = """ + INSERT INTO CATEGORY (ID, NAME, IS_DEFAULT, "ORDER", INCLUDE_IN_UPDATE) VALUES (0, 'Default', TRUE, 0, -1) + """.trimIndent() +}