Fix series index and add it to the sort system.

This commit is contained in:
Thomas Andres Gomez 2022-10-16 18:44:52 +02:00
parent e69af172af
commit 46a1f568f1
16 changed files with 74 additions and 56 deletions

View file

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "3f612998c4903a247d1c955da20b272d", "identityHash": "96272f43076988345569b6ad637a4e01",
"entities": [ "entities": [
{ {
"tableName": "AUTHOR", "tableName": "AUTHOR",
@ -38,7 +38,7 @@
}, },
{ {
"tableName": "BOOK", "tableName": "BOOK",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`BOOK_ID` INTEGER NOT NULL, `BOOK_TITLE` TEXT NOT NULL, `BOOK_SORT` TEXT NOT NULL, `BOOK_HAVE_COVER` INTEGER NOT NULL, `BOOK_RELEASE_DATE` INTEGER NOT NULL, `BOOK_LANGUAGE_ID` INTEGER, `BOOK_RATING` INTEGER, `BOOK_SERIES_ID` INTEGER, `BOOK_SYNOPSIS` TEXT, `BOOK_ISNEW` INTEGER NOT NULL, `BOOK_NEW_ORDER` INTEGER, PRIMARY KEY(`BOOK_ID`))", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`BOOK_ID` INTEGER NOT NULL, `BOOK_TITLE` TEXT NOT NULL, `BOOK_SORT` TEXT NOT NULL, `BOOK_HAVE_COVER` INTEGER NOT NULL, `BOOK_RELEASE_DATE` INTEGER NOT NULL, `BOOK_LANGUAGE_ID` INTEGER, `BOOK_RATING` INTEGER, `BOOK_SERIES_ID` INTEGER, `BOOK_SERIES_INDEX` INTEGER, `BOOK_SYNOPSIS` TEXT, `BOOK_IS_NEW` INTEGER NOT NULL, `BOOK_NEW_ORDER` INTEGER, PRIMARY KEY(`BOOK_ID`))",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -88,6 +88,12 @@
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": false "notNull": false
}, },
{
"fieldPath": "seriesIndex",
"columnName": "BOOK_SERIES_INDEX",
"affinity": "INTEGER",
"notNull": false
},
{ {
"fieldPath": "synopsis", "fieldPath": "synopsis",
"columnName": "BOOK_SYNOPSIS", "columnName": "BOOK_SYNOPSIS",
@ -96,7 +102,7 @@
}, },
{ {
"fieldPath": "isNew", "fieldPath": "isNew",
"columnName": "BOOK_ISNEW", "columnName": "BOOK_IS_NEW",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
}, },
@ -170,7 +176,7 @@
}, },
{ {
"tableName": "SERIES", "tableName": "SERIES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`SERIES_ID` INTEGER NOT NULL, `SERIES_NAME` TEXT NOT NULL, `SERIES_SORT` TEXT NOT NULL, `SERIES_INDEX` INTEGER, PRIMARY KEY(`SERIES_ID`))", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`SERIES_ID` INTEGER NOT NULL, `SERIES_NAME` TEXT NOT NULL, `SERIES_SORT` TEXT NOT NULL, PRIMARY KEY(`SERIES_ID`))",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -189,12 +195,6 @@
"columnName": "SERIES_SORT", "columnName": "SERIES_SORT",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
},
{
"fieldPath": "index",
"columnName": "SERIES_INDEX",
"affinity": "INTEGER",
"notNull": false
} }
], ],
"primaryKey": { "primaryKey": {
@ -284,7 +284,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3f612998c4903a247d1c955da20b272d')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '96272f43076988345569b6ad637a4e01')"
] ]
} }
} }

View file

@ -25,6 +25,8 @@ data class BookDbo(
// details // details
@ColumnInfo(name = SERIES_ID) @ColumnInfo(name = SERIES_ID)
val series: Int? = null, // one-to-many val series: Int? = null, // one-to-many
@ColumnInfo(name = SERIES_INDEX)
val seriesIndex: Int? = null,
@ColumnInfo(name = SYNOPSIS) @ColumnInfo(name = SYNOPSIS)
val synopsis: String? = null, val synopsis: String? = null,
// source // source
@ -54,8 +56,9 @@ data class BookDbo(
const val LANGUAGE_ID = "${TABLE}_LANGUAGE_ID" const val LANGUAGE_ID = "${TABLE}_LANGUAGE_ID"
const val RATING = "${TABLE}_RATING" const val RATING = "${TABLE}_RATING"
const val SERIES_ID = "${TABLE}_SERIES_ID" const val SERIES_ID = "${TABLE}_SERIES_ID"
const val SERIES_INDEX = "${TABLE}_SERIES_INDEX"
const val SYNOPSIS = "${TABLE}_SYNOPSIS" const val SYNOPSIS = "${TABLE}_SYNOPSIS"
const val IS_NEW = "${TABLE}_ISNEW" const val IS_NEW = "${TABLE}_IS_NEW"
const val NEW_ORDER = "${TABLE}_NEW_ORDER" const val NEW_ORDER = "${TABLE}_NEW_ORDER"
} }
} }

View file

@ -13,14 +13,11 @@ data class SeriesDbo(
val name: String, val name: String,
@ColumnInfo(name = SORT) @ColumnInfo(name = SORT)
val sort: String, val sort: String,
@ColumnInfo(name = INDEX)
val index: Int?,
) { ) {
companion object { companion object {
const val TABLE = "SERIES" const val TABLE = "SERIES"
const val ID = "${TABLE}_ID" const val ID = "${TABLE}_ID"
const val NAME = "${TABLE}_NAME" const val NAME = "${TABLE}_NAME"
const val SORT = "${TABLE}_SORT" const val SORT = "${TABLE}_SORT"
const val INDEX = "${TABLE}_INDEX"
} }
} }

View file

@ -14,6 +14,7 @@ data class Book(
val genre: List<Genre>? = null, val genre: List<Genre>? = null,
// details // details
val series: Series? = null, val series: Series? = null,
val seriesIndex: Int? = null,
val synopsis: String? = null, val synopsis: String? = null,
// source // source
val isNew: Boolean = false, val isNew: Boolean = false,

View file

@ -4,5 +4,4 @@ data class Series(
val id: Int, val id: Int,
val name: String, val name: String,
val sort: String, val sort: String,
val index: Int?,
) )

View file

@ -7,8 +7,5 @@ import androidx.compose.runtime.Stable
@Immutable @Immutable
enum class SortType { enum class SortType {
Book, Book,
Author,
Series, Series,
Genre,
Language;
} }

View file

@ -13,7 +13,7 @@ class BookFactory {
fun fromListResponseToBook( fun fromListResponseToBook(
response: BookListResponse.Book, response: BookListResponse.Book,
seriesHash: Map<Int, Series>, seriesHash: SeriesHash,
genreHash: Map<Int, List<Genre>>, genreHash: Map<Int, List<Genre>>,
isNew: Boolean = false, isNew: Boolean = false,
newOrder: Int? = null newOrder: Int? = null
@ -63,12 +63,6 @@ class BookFactory {
} }
val rating = response.rating?.toIntOrNull() val rating = response.rating?.toIntOrNull()
val newOrder = if (isNew) {
newOrder
} else {
null
}
return Book( return Book(
id = id ?: throw error("id"), id = id ?: throw error("id"),
title = title ?: throw error("title"), title = title ?: throw error("title"),
@ -76,12 +70,13 @@ class BookFactory {
author = author ?: throw error("author"), author = author ?: throw error("author"),
haveCover = cover ?: throw error("haveCover"), haveCover = cover ?: throw error("haveCover"),
releaseDate = releaseDate ?: throw error("releaseDate"), releaseDate = releaseDate ?: throw error("releaseDate"),
series = seriesHash[id], series = seriesHash[id]?.second,
seriesIndex = seriesHash[id]?.first,
language = language, language = language,
rating = rating, rating = rating,
genre = genreHash[id], genre = genreHash[id],
isNew = isNew, isNew = isNew,
newOrder = newOrder, newOrder = if (isNew) newOrder else null,
) )
} }
@ -126,7 +121,7 @@ class BookFactory {
val seriesSort = book.series_sort val seriesSort = book.series_sort
val seriesIndex: Int? = book.book_series_index?.toInt() val seriesIndex: Int? = book.book_series_index?.toInt()
val series: Series? = if (seriesId != null && seriesName != null && seriesSort != null) { val series: Series? = if (seriesId != null && seriesName != null && seriesSort != null) {
Series(id = seriesId, name = seriesName, sort = seriesSort, seriesIndex) Series(id = seriesId, name = seriesName, sort = seriesSort)
} else { } else {
null null
} }
@ -151,6 +146,7 @@ class BookFactory {
language = language ?: throw error("language"), language = language ?: throw error("language"),
rating = rating, rating = rating,
series = series, series = series,
seriesIndex = seriesIndex,
genre = tag, genre = tag,
synopsis = synopsis, synopsis = synopsis,
isNew = isNew isNew = isNew

View file

@ -4,26 +4,28 @@ import com.pixelized.biblib.model.book.Series
import com.pixelized.biblib.network.data.response.SeriesListResponse import com.pixelized.biblib.network.data.response.SeriesListResponse
import com.pixelized.biblib.utils.exception.missingField import com.pixelized.biblib.utils.exception.missingField
typealias SeriesHash = Map<Int, Pair<Int, Series>>
class SeriesFactory { class SeriesFactory {
fun fromListResponseToSeriesHash( fun fromListResponseToSeriesHash(
response: SeriesListResponse, response: SeriesListResponse,
): Map<Int, Series> { ): SeriesHash {
fun error(name: String) = missingField("#fromListResponseToSeriesHash()", name, response) fun error(name: String) = missingField("#fromListResponseToSeriesHash()", name, response)
val hash = mutableMapOf<Int, Series>() // BOOK_ID, BOOK_SERIES_INDEX, SERIES
val hash = mutableMapOf<Int, Pair<Int, Series>>()
response.series?.forEachIndexed { index, data -> response.series?.forEach { data ->
// build the Series items // build the Series items
val series = Series( val series = Series(
id = data.series_id ?: throw error("id"), id = data.series_id ?: throw error("id"),
name = data.series_name ?: throw error("name"), name = data.series_name ?: throw error("name"),
sort = data.series_sort ?: throw error("sort"), sort = data.series_sort ?: throw error("sort"),
index = index,
) )
// link that item to the book id. // link that item to the book id.
data.books?.forEach { book -> data.books?.forEachIndexed { index, book ->
book.book_id?.let { id -> hash[id] = series } book.book_id?.let { id -> hash[id] = (index + 1) to series }
} }
} }

View file

@ -61,9 +61,7 @@ class BookRepository @Inject constructor(
languages.add(it.toDbo()) languages.add(it.toDbo())
} }
book.series?.let { book.series?.let {
if (it.id != null) { series.add(it.toDbo(it.id))
series.add(it.toDbo(it.id))
}
} }
books.add(book.toDbo()) books.add(book.toDbo())
} }
@ -93,7 +91,6 @@ class BookRepository @Inject constructor(
id = id, id = id,
name = name, name = name,
sort = sort, sort = sort,
index = index,
) )
private fun Language.toDbo() = LanguageDbo( private fun Language.toDbo() = LanguageDbo(
@ -110,6 +107,7 @@ class BookRepository @Inject constructor(
language = language?.id, language = language?.id,
rating = rating, rating = rating,
series = series?.id, series = series?.id,
seriesIndex = seriesIndex,
synopsis = synopsis, synopsis = synopsis,
isNew = isNew, isNew = isNew,
newOrder = newOrder, newOrder = newOrder,
@ -126,6 +124,7 @@ class BookRepository @Inject constructor(
rating = book.rating, rating = book.rating,
genre = genres?.map { it.toGenre() }, genre = genres?.map { it.toGenre() },
series = series?.toSeries(), series = series?.toSeries(),
seriesIndex = book.seriesIndex,
synopsis = book.synopsis, synopsis = book.synopsis,
isNew = book.isNew, isNew = book.isNew,
) )
@ -150,6 +149,5 @@ class BookRepository @Inject constructor(
id = id, id = id,
name = name, name = name,
sort = sort, sort = sort,
index = index,
) )
} }

View file

@ -11,6 +11,7 @@ interface ISearchRepository {
seriesId: Int?, seriesId: Int?,
genreId: Int?, genreId: Int?,
languageId: Int?, languageId: Int?,
sortBy : SortType,
limit: Int, limit: Int,
offset: Int, offset: Int,
): List<Book> ): List<Book>

View file

@ -20,6 +20,7 @@ class SearchRepository @Inject constructor(
seriesId: Int?, seriesId: Int?,
genreId: Int?, genreId: Int?,
languageId: Int?, languageId: Int?,
sortBy : SortType,
limit: Int, limit: Int,
offset: Int offset: Int
): List<Book> { ): List<Book> {
@ -43,7 +44,10 @@ class SearchRepository @Inject constructor(
query += args.where(argument = languageId) { query += args.where(argument = languageId) {
BookDbo.run { "$TABLE.$LANGUAGE_ID LIKE ?" } BookDbo.run { "$TABLE.$LANGUAGE_ID LIKE ?" }
} }
query += BookDbo.run { " ORDER BY $TABLE.$SORT" } query += when (sortBy) {
SortType.Book -> BookDbo.run { " ORDER BY $TABLE.$SORT" }
SortType.Series -> BookDbo.run { " ORDER BY $TABLE.$SERIES_INDEX" }
}
// Limit and Offset the query. // Limit and Offset the query.
query += " LIMIT $limit OFFSET $offset;" query += " LIMIT $limit OFFSET $offset;"
// compute the query // compute the query
@ -143,6 +147,7 @@ private fun BookRelation.toBook(): Book = Book(
rating = book.rating, rating = book.rating,
genre = genres?.map { it.toGenre() }, genre = genres?.map { it.toGenre() },
series = series?.toSeries(), series = series?.toSeries(),
seriesIndex = book.seriesIndex,
synopsis = book.synopsis, synopsis = book.synopsis,
isNew = book.isNew, isNew = book.isNew,
) )
@ -167,5 +172,4 @@ private fun SeriesDbo.toSeries() = Series(
id = id, id = id,
name = name, name = name,
sort = sort, sort = sort,
index = index,
) )

View file

@ -29,10 +29,11 @@ import com.skydoves.landscapist.glide.GlideImage
@Stable @Stable
data class MicroBookThumbnailUio( data class MicroBookThumbnailUio(
val id: Int, val id: Int,
val cover: String,
val title: String, val title: String,
val author: String, val author: String,
val series: String,
val isNew: Boolean, val isNew: Boolean,
val cover: String,
) )
@Composable @Composable
@ -71,7 +72,7 @@ private fun MicroBookThumbnailContent(
ConstraintLayout( ConstraintLayout(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { ) {
val (cover, title, author) = createRefs() val (cover, title, author, series) = createRefs()
GlideImage( GlideImage(
modifier = Modifier modifier = Modifier
@ -95,25 +96,37 @@ private fun MicroBookThumbnailContent(
}, },
style = MaterialTheme.typography.body1, style = MaterialTheme.typography.body1,
color = MaterialTheme.bibLib.colors.typography.medium, color = MaterialTheme.bibLib.colors.typography.medium,
text = thumbnail.title,
maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = thumbnail.title,
) )
Text( Text(
modifier = Modifier.constrainAs(author) { modifier = Modifier.constrainAs(author) {
top.linkTo(title.bottom, margin = dimen.dp4) top.linkTo(title.bottom, margin = dimen.dp4)
bottom.linkTo(parent.bottom, margin = dimen.dp4)
start.linkTo(cover.end, margin = dimen.dp8) start.linkTo(cover.end, margin = dimen.dp8)
end.linkTo(parent.end, margin = dimen.dp8) end.linkTo(parent.end, margin = dimen.dp8)
width = Dimension.fillToConstraints width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
}, },
style = MaterialTheme.typography.caption, style = MaterialTheme.typography.caption,
color = MaterialTheme.bibLib.colors.typography.easy, color = MaterialTheme.bibLib.colors.typography.easy,
maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
text = thumbnail.author maxLines = 1,
text = thumbnail.author,
)
Text(
modifier = Modifier.constrainAs(series) {
top.linkTo(author.bottom, margin = dimen.dp4)
start.linkTo(cover.end, margin = dimen.dp8)
end.linkTo(parent.end, margin = dimen.dp8)
width = Dimension.fillToConstraints
},
style = MaterialTheme.typography.caption,
color = MaterialTheme.bibLib.colors.typography.easy,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = thumbnail.series,
) )
} }
} }
@ -186,10 +199,11 @@ private fun MicroBookThumbnailPreview() {
MicroBookThumbnail( MicroBookThumbnail(
thumbnail = MicroBookThumbnailUio( thumbnail = MicroBookThumbnailUio(
id = 0, id = 0,
cover = "",
title = "Foundation", title = "Foundation",
author = "Asimov", author = "Asimov",
series = "Foundation - 1",
isNew = false, isNew = false,
cover = "",
) )
) )
} }

View file

@ -4,6 +4,7 @@ import android.util.Log
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.paging.PagingState import androidx.paging.PagingState
import com.pixelized.biblib.model.book.Book import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.model.search.SortType
import com.pixelized.biblib.repository.search.ISearchRepository import com.pixelized.biblib.repository.search.ISearchRepository
import com.pixelized.biblib.utils.extention.page import com.pixelized.biblib.utils.extention.page
@ -13,6 +14,7 @@ class BookSearchSource(
private val authorId: Int?, private val authorId: Int?,
private val seriesId: Int?, private val seriesId: Int?,
private val genreId: Int?, private val genreId: Int?,
private val sortBy: SortType,
private val languageId: Int?, private val languageId: Int?,
private val limit: Int, private val limit: Int,
) : PagingSource<Int, Book>() { ) : PagingSource<Int, Book>() {
@ -30,6 +32,7 @@ class BookSearchSource(
seriesId = seriesId, seriesId = seriesId,
genreId = genreId, genreId = genreId,
languageId = languageId, languageId = languageId,
sortBy = sortBy,
limit = limit, limit = limit,
offset = index * limit offset = index * limit
) )

View file

@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.paging.* import androidx.paging.*
import com.pixelized.biblib.model.book.Book import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.model.search.SortType
import com.pixelized.biblib.repository.search.ISearchRepository import com.pixelized.biblib.repository.search.ISearchRepository
import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnailUio import com.pixelized.biblib.ui.screen.home.common.item.MicroBookThumbnailUio
import com.pixelized.biblib.ui.screen.home.page.search.bottom.FilterUio import com.pixelized.biblib.ui.screen.home.page.search.bottom.FilterUio
@ -83,6 +84,7 @@ class BookSearchViewModel @Inject constructor(
authorId = author?.id, authorId = author?.id,
seriesId = series?.id, seriesId = series?.id,
genreId = genre?.id, genreId = genre?.id,
sortBy = if (series != null) SortType.Series else SortType.Book,
languageId = language?.id, languageId = language?.id,
limit = SEARCH_PAGE_SIZE, limit = SEARCH_PAGE_SIZE,
).also { ).also {

View file

@ -38,7 +38,7 @@ data class BibLibDimen(
val padding: Dp = 16.dp, val padding: Dp = 16.dp,
val arrangement: Dp = 16.dp, val arrangement: Dp = 16.dp,
val cover: DpSize = 72.dp.let { DpSize(width = it, height = it * ratio) }, val cover: DpSize = 72.dp.let { DpSize(width = it, height = it * ratio) },
val micro: DpSize = 48.dp.let { DpSize(width = it, height = it * ratio) }, val micro: DpSize = 50.dp.let { DpSize(width = it, height = it * ratio) },
) )
@Stable @Stable

View file

@ -24,6 +24,7 @@ fun Book.toMicroThumbnailUio(
cover = "${coverBaseUrl}/$id.jpg", cover = "${coverBaseUrl}/$id.jpg",
title = title, title = title,
author = author.joinToString { it.name }, author = author.joinToString { it.name },
series = toLabel(series) ?: "",
isNew = isNew, isNew = isNew,
) )
@ -59,15 +60,15 @@ fun Book.toDetailUio(
rating = rating?.toFloat() ?: 0.0f, rating = rating?.toFloat() ?: 0.0f,
language = language?.displayLanguage?.capitalize() ?: "", language = language?.displayLanguage?.capitalize() ?: "",
date = releaseDate.shortDate(), date = releaseDate.shortDate(),
series = series.toLabel(), series = toLabel(series),
description = synopsis ?: "", description = synopsis ?: "",
cover = "${coverBaseUrl}/$id.jpg", cover = "${coverBaseUrl}/$id.jpg",
) )
fun Series?.toLabel(): String? { fun Book.toLabel(series: Series?): String? {
return when { return when {
this != null && index != null -> "$name - $index" series?.name != null && seriesIndex != null -> "${series.name} - $seriesIndex"
this != null -> name series?.name != null -> series.name
else -> null else -> null
} }
} }