set default category when adding new manga

This commit is contained in:
Aria Moradi 2021-05-23 15:28:46 +04:30
parent fdfc256c4d
commit 23b643d637
11 changed files with 102 additions and 49 deletions

View File

@ -35,16 +35,16 @@ object Category {
}
}
fun updateCategory(categoryId: Int, name: String?, isLanding: Boolean?) {
fun updateCategory(categoryId: Int, name: String?, isDefault: Boolean?) {
transaction {
CategoryTable.update({ CategoryTable.id eq categoryId }) {
if (name != null) it[CategoryTable.name] = name
if (isLanding != null) it[CategoryTable.isLanding] = isLanding
if (isDefault != null) it[CategoryTable.isDefault] = isDefault
}
}
}
/**
/**
* Move the category from position `from` to `to`
*/
fun reorderCategory(categoryId: Int, from: Int, to: Int) {

View File

@ -9,25 +9,37 @@ package ir.armor.tachidesk.impl
import ir.armor.tachidesk.impl.Manga.getManga
import ir.armor.tachidesk.model.database.table.CategoryMangaTable
import ir.armor.tachidesk.model.database.table.CategoryTable
import ir.armor.tachidesk.model.database.table.MangaTable
import ir.armor.tachidesk.model.database.table.toDataClass
import ir.armor.tachidesk.model.dataclass.MangaDataClass
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
object Library {
// TODO: `Category.isLanding` is to handle the default categories a new library manga gets,
// TODO: `Category.isLanding` is to handle the default categories a new library manga gets,
// ..implement that shit at some time...
// ..also Consider to rename it to `isDefault`
suspend fun addMangaToLibrary(mangaId: Int) {
val manga = getManga(mangaId)
if (!manga.inLibrary) {
transaction {
val defaultCategories = CategoryTable.select { CategoryTable.isDefault eq true }.toList()
MangaTable.update({ MangaTable.id eq manga.id }) {
it[inLibrary] = true
it[MangaTable.inLibrary] = true
it[MangaTable.defaultCategory] = defaultCategories.isEmpty()
}
defaultCategories.forEach { category ->
CategoryMangaTable.insert {
it[CategoryMangaTable.category] = category[CategoryTable.id].value
it[CategoryMangaTable.manga] = mangaId
}
}
}
}

View File

@ -13,7 +13,7 @@ import ir.armor.tachidesk.impl.util.lang.awaitSingle
import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass
object Search {
// TODO
// TODO
fun sourceFilters(sourceId: Long) {
val source = getHttpSource(sourceId)
// source.getFilterList().toItems()
@ -34,7 +34,7 @@ object Search {
val filter: Any
)
/**
/**
* Note: Exhentai had a filter serializer (now in SY) that we might be able to steal
*/
// private fun FilterList.toFilterWrapper(): List<FilterWrapper> {

View File

@ -0,0 +1,24 @@
package ir.armor.tachidesk.model.database.migration
import ir.armor.tachidesk.model.database.migration.lib.Migration
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.vendors.currentDialect
/*
* 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 M0003_DefaultCategory : Migration() {
/** this migration renamed CategoryTable.IS_LANDING to ChapterTable.IS_DEFAULT */
override fun run() {
with(TransactionManager.current()) {
exec("ALTER TABLE CATEGORY ALTER COLUMN IS_LANDING RENAME TO IS_DEFAULT")
commit()
currentDialect.resetCaches()
}
}
}

View File

@ -13,13 +13,13 @@ import org.jetbrains.exposed.sql.ResultRow
object CategoryTable : IntIdTable() {
val name = varchar("name", 64)
val isLanding = bool("is_landing").default(false)
val order = integer("order").default(0)
val isDefault = bool("is_default").default(false)
}
fun CategoryTable.toDataClass(categoryEntry: ResultRow) = CategoryDataClass(
categoryEntry[this.id].value,
categoryEntry[this.order],
categoryEntry[this.name],
categoryEntry[this.isLanding],
categoryEntry[this.isDefault],
)

View File

@ -11,5 +11,5 @@ data class CategoryDataClass(
val id: Int,
val order: Int,
val name: String,
val isLanding: Boolean
val default: Boolean
)

View File

@ -13,9 +13,8 @@ import ir.armor.tachidesk.impl.CategoryManga.removeMangaFromCategory
import ir.armor.tachidesk.impl.Chapter.getChapter
import ir.armor.tachidesk.impl.Chapter.getChapterList
import ir.armor.tachidesk.impl.Chapter.modifyChapter
import ir.armor.tachidesk.impl.Library.addMangaToLibrary
import ir.armor.tachidesk.impl.Library
import ir.armor.tachidesk.impl.Library.getLibraryMangas
import ir.armor.tachidesk.impl.Library.removeMangaFromLibrary
import ir.armor.tachidesk.impl.Manga.getManga
import ir.armor.tachidesk.impl.Manga.getMangaThumbnail
import ir.armor.tachidesk.impl.MangaList.getMangaList
@ -216,24 +215,6 @@ object JavalinSetup {
)
}
// adds the manga to library
app.get("api/v1/manga/:mangaId/library") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
ctx.result(
future { addMangaToLibrary(mangaId) }
)
}
// removes the manga from the library
app.delete("api/v1/manga/:mangaId/library") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
ctx.result(
future { removeMangaFromLibrary(mangaId) }
)
}
// list manga's categories
app.get("api/v1/manga/:mangaId/category/") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
@ -332,6 +313,24 @@ object JavalinSetup {
ctx.json(sourceFilters(sourceId))
}
// adds the manga to library
app.get("api/v1/manga/:mangaId/library") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
ctx.result(
future { Library.addMangaToLibrary(mangaId) }
)
}
// removes the manga from the library
app.delete("api/v1/manga/:mangaId/library") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
ctx.result(
future { Library.removeMangaFromLibrary(mangaId) }
)
}
// lists mangas that have no category assigned
app.get("/api/v1/library/") { ctx ->
ctx.json(getLibraryMangas())
@ -358,8 +357,8 @@ object JavalinSetup {
app.patch("/api/v1/category/:categoryId") { ctx ->
val categoryId = ctx.pathParam("categoryId").toInt()
val name = ctx.formParam("name")
val isLanding = if (ctx.formParam("isLanding") != null) ctx.formParam("isLanding")?.toBoolean() else null
updateCategory(categoryId, name, isLanding)
val isDefault = ctx.formParam("default")?.toBoolean()
updateCategory(categoryId, name, isDefault)
ctx.status(200)
}

View File

@ -72,7 +72,7 @@ export default function Library() {
const defaultCategoryTab = {
category: {
name: 'Default',
isLanding: true,
default: true,
order: 0,
id: -1,
},

View File

@ -7,7 +7,8 @@
import React, { useContext, useEffect, useState } from 'react';
import List from '@material-ui/core/List';
import InboxIcon from '@material-ui/icons/Inbox';
import ListAltIcon from '@material-ui/icons/ListAlt';
import BackupIcon from '@material-ui/icons/Backup';
import Brightness6Icon from '@material-ui/icons/Brightness6';
import DnsIcon from '@material-ui/icons/Dns';
import EditIcon from '@material-ui/icons/Edit';
@ -50,13 +51,13 @@ export default function Settings() {
<List style={{ padding: 0 }}>
<ListItemLink href="/settings/categories">
<ListItemIcon>
<InboxIcon />
<ListAltIcon />
</ListItemIcon>
<ListItemText primary="Categories" />
</ListItemLink>
<ListItemLink href="/settings/backup">
<ListItemIcon>
<InboxIcon />
<BackupIcon />
</ListItemIcon>
<ListItemText primary="Backup" />
</ListItemLink>

View File

@ -28,8 +28,9 @@ import TextField from '@material-ui/core/TextField';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Checkbox from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import NavbarContext from '../../context/NavbarContext';
import client from '../../util/client';
@ -49,7 +50,8 @@ export default function Categories() {
const [categories, setCategories] = useState([]);
const [categoryToEdit, setCategoryToEdit] = useState(-1); // -1 means new category
const [dialogOpen, setDialogOpen] = useState(false);
const [dialogValue, setDialogValue] = useState('');
const [dialogName, setDialogName] = useState('');
const [dialogDefault, setDialogDefault] = useState(false);
const theme = useTheme();
const [updateTriggerHolder, setUpdateTriggerHolder] = useState(0); // just a hack
@ -93,7 +95,8 @@ export default function Categories() {
};
const resetDialog = () => {
setDialogValue('');
setDialogName('');
setDialogDefault(false);
setCategoryToEdit(-1);
};
@ -102,6 +105,13 @@ export default function Categories() {
setDialogOpen(true);
};
const handleEditDialogOpen = (index) => {
setDialogName(categories[index].name);
setDialogDefault(categories[index].default);
setCategoryToEdit(index);
setDialogOpen(true);
};
const handleDialogCancel = () => {
setDialogOpen(false);
};
@ -110,7 +120,8 @@ export default function Categories() {
setDialogOpen(false);
const formData = new FormData();
formData.append('name', dialogValue);
formData.append('name', dialogName);
formData.append('default', dialogDefault);
if (categoryToEdit === -1) {
client.post('/api/v1/category/', formData)
@ -161,8 +172,7 @@ export default function Categories() {
/>
<IconButton
onClick={() => {
handleDialogOpen();
setCategoryToEdit(index);
handleEditDialogOpen(index);
}}
>
<EditIcon />
@ -197,12 +207,9 @@ export default function Categories() {
</Fab>
<Dialog open={dialogOpen} onClose={handleDialogCancel}>
<DialogTitle id="form-dialog-title">
{categoryToEdit === -1 ? 'New Catalog' : `Rename: ${categories[categoryToEdit].name}`}
{categoryToEdit === -1 ? 'New Catalog' : 'Edit Catalog'}
</DialogTitle>
<DialogContent>
<DialogContentText>
Enter new category name.
</DialogContentText>
<TextField
autoFocus
margin="dense"
@ -210,8 +217,18 @@ export default function Categories() {
label="Category Name"
type="text"
fullWidth
value={dialogValue}
onChange={(e) => setDialogValue(e.target.value)}
value={dialogName}
onChange={(e) => setDialogName(e.target.value)}
/>
<FormControlLabel
control={(
<Checkbox
checked={dialogDefault}
onChange={(e) => setDialogDefault(e.target.checked)}
color="default"
/>
)}
label="Default category when adding new manga to library"
/>
</DialogContent>
<DialogActions>

View File

@ -80,7 +80,7 @@ interface ICategory {
id: number
order: number
name: String
isLanding: boolean
default: boolean
}
interface INavbarOverride {