标签问题
This commit is contained in:
@@ -98,7 +98,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
where type & ${BookType.text} > 0
|
||||
and type & ${BookType.local} = 0
|
||||
@@ -134,7 +135,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
ORDER BY durChapterTime DESC
|
||||
"""
|
||||
@@ -167,7 +169,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.audio} > 0
|
||||
"""
|
||||
@@ -200,7 +203,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.local} > 0
|
||||
"""
|
||||
@@ -238,7 +242,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
where type & ${BookType.audio} = 0 and type & ${BookType.local} = 0
|
||||
and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0
|
||||
@@ -277,7 +282,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
where type & ${BookType.local} > 0
|
||||
and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0
|
||||
@@ -311,7 +317,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
WHERE (`group` & :group) > 0
|
||||
"""
|
||||
@@ -346,7 +353,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
WHERE name like '%'||:key||'%' or author like '%'||:key||'%' or originName like '%'||:key||'%'
|
||||
"""
|
||||
@@ -379,7 +387,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
where type & ${BookType.updateError} > 0
|
||||
order by durChapterTime desc
|
||||
@@ -413,7 +422,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
WHERE durChapterIndex = 0 AND durChapterPos = 0
|
||||
"""
|
||||
@@ -446,7 +456,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
WHERE totalChapterNum > 0 AND durChapterIndex >= totalChapterNum - 1
|
||||
"""
|
||||
@@ -479,7 +490,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
WHERE totalChapterNum > 0 AND durChapterIndex > 0 AND durChapterIndex < totalChapterNum - 1
|
||||
"""
|
||||
@@ -512,7 +524,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.image} > 0
|
||||
"""
|
||||
@@ -545,7 +558,8 @@ interface BookDao {
|
||||
`order`,
|
||||
canUpdate,
|
||||
ifnull(customIntro, intro) as intro,
|
||||
kind
|
||||
kind,
|
||||
wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.text} > 0
|
||||
"""
|
||||
@@ -721,7 +735,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
ORDER BY durChapterTime DESC
|
||||
LIMIT 10
|
||||
@@ -736,7 +750,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.text} > 0 AND type & ${BookType.local} = 0
|
||||
AND ((SELECT COALESCE(SUM(groupId), 0) FROM book_groups WHERE groupId > 0) & `group`) = 0
|
||||
@@ -754,7 +768,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.local} > 0
|
||||
ORDER BY durChapterTime DESC
|
||||
@@ -770,7 +784,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.audio} > 0
|
||||
ORDER BY durChapterTime DESC
|
||||
@@ -786,7 +800,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.audio} = 0 AND type & ${BookType.local} = 0
|
||||
AND ((SELECT COALESCE(SUM(groupId), 0) FROM book_groups WHERE groupId > 0) & `group`) = 0
|
||||
@@ -803,7 +817,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.local} > 0
|
||||
AND ((SELECT COALESCE(SUM(groupId), 0) FROM book_groups WHERE groupId > 0) & `group`) = 0
|
||||
@@ -820,7 +834,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.image} > 0
|
||||
ORDER BY durChapterTime DESC
|
||||
@@ -836,7 +850,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.text} > 0
|
||||
ORDER BY durChapterTime DESC
|
||||
@@ -852,7 +866,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE type & ${BookType.updateError} > 0
|
||||
ORDER BY durChapterTime DESC
|
||||
@@ -868,7 +882,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE durChapterIndex = 0 AND durChapterPos = 0
|
||||
ORDER BY durChapterTime DESC
|
||||
@@ -884,7 +898,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE totalChapterNum > 0 AND durChapterIndex > 0 AND durChapterIndex < totalChapterNum - 1
|
||||
ORDER BY durChapterTime DESC
|
||||
@@ -900,7 +914,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE totalChapterNum > 0 AND durChapterIndex >= totalChapterNum - 1
|
||||
ORDER BY durChapterTime DESC
|
||||
@@ -916,7 +930,7 @@ interface BookDao {
|
||||
durChapterPos, latestChapterTitle, latestChapterTime,
|
||||
lastCheckCount, totalChapterNum, durChapterIndex,
|
||||
type, `group`, `order`, canUpdate,
|
||||
ifnull(customIntro, intro) as intro, kind
|
||||
ifnull(customIntro, intro) as intro, kind, wordCount
|
||||
FROM books
|
||||
WHERE (`group` & :groupId) > 0
|
||||
ORDER BY durChapterTime DESC
|
||||
|
||||
@@ -355,6 +355,9 @@ data class Book(
|
||||
newBook.type = type
|
||||
}
|
||||
newBook.readConfig = readConfig
|
||||
if (newBook.wordCount.isNullOrBlank()) {
|
||||
newBook.wordCount = wordCount
|
||||
}
|
||||
return newBook
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
package io.legado.app.help.book
|
||||
|
||||
import io.legado.app.ui.config.otherConfig.OtherConfig
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.script.buildScriptBindings
|
||||
@@ -19,6 +18,7 @@ import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.help.RuleBigDataHelp
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.model.localBook.LocalBook
|
||||
import io.legado.app.ui.config.otherConfig.OtherConfig
|
||||
import io.legado.app.utils.FileDoc
|
||||
import io.legado.app.utils.GSON
|
||||
import io.legado.app.utils.MD5Utils
|
||||
@@ -27,6 +27,7 @@ import io.legado.app.utils.find
|
||||
import io.legado.app.utils.inputStream
|
||||
import io.legado.app.utils.isUri
|
||||
import io.legado.app.utils.normalizeFileName
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import splitties.init.appCtx
|
||||
import java.io.File
|
||||
@@ -306,6 +307,44 @@ fun Book.upType() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Book.upKind() {
|
||||
val fileSizePattern = Regex("""^\d[\d,.]*\s*(b|kb|M|G|T)$""", RegexOption.IGNORE_CASE)
|
||||
val wordCountPattern = Regex(""".*?\d.*字$""")
|
||||
val kinds = kind?.splitNotBlank(",", "\n").orEmpty()
|
||||
.filter {
|
||||
it.isNotBlank() && !fileSizePattern.matches(it.trim()) && !wordCountPattern.matches(
|
||||
it.trim()
|
||||
)
|
||||
}
|
||||
.toMutableList()
|
||||
|
||||
if (isLocal) {
|
||||
// 添加格式
|
||||
val typeName = getBookTypeName()
|
||||
if (typeName != "未知类型" && !kinds.contains(typeName)) {
|
||||
kinds.add(0, typeName)
|
||||
}
|
||||
// 添加大小
|
||||
try {
|
||||
val size = FileDoc.fromFile(bookUrl).size
|
||||
if (size > 0) {
|
||||
kinds.add(io.legado.app.utils.ConvertUtils.formatFileSize(size))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// 添加字数
|
||||
wordCount?.let {
|
||||
if (it.isNotBlank()) {
|
||||
kinds.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
kind = kinds.distinct().joinToString(",")
|
||||
}
|
||||
|
||||
fun Book.sync(oldBook: Book) {
|
||||
val curBook = appDb.bookDao.getBook(oldBook.bookUrl)!!
|
||||
durChapterTime = curBook.durChapterTime
|
||||
@@ -344,6 +383,10 @@ fun Book.updateTo(newBook: Book): Book {
|
||||
newBook.type = type
|
||||
}
|
||||
newBook.readConfig = readConfig
|
||||
if (newBook.wordCount.isNullOrBlank()) {
|
||||
newBook.wordCount = wordCount
|
||||
}
|
||||
newBook.upKind()
|
||||
val variableMap = variableMap.toMutableMap()
|
||||
variableMap.keys.removeIf {
|
||||
newBook.hasVariable(it)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.legado.app.model.localBook
|
||||
|
||||
import io.legado.app.ui.config.otherConfig.OtherConfig
|
||||
import android.net.Uri
|
||||
import android.util.Base64
|
||||
import androidx.core.net.toUri
|
||||
@@ -36,11 +35,13 @@ import io.legado.app.help.book.isPdf
|
||||
import io.legado.app.help.book.isUmd
|
||||
import io.legado.app.help.book.removeLocalUriCache
|
||||
import io.legado.app.help.book.simulatedTotalChapterNum
|
||||
import io.legado.app.help.book.upKind
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.lib.webdav.WebDav
|
||||
import io.legado.app.lib.webdav.WebDavException
|
||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||
import io.legado.app.model.analyzeRule.CustomUrl
|
||||
import io.legado.app.ui.config.otherConfig.OtherConfig
|
||||
import io.legado.app.utils.ArchiveUtils
|
||||
import io.legado.app.utils.FileDoc
|
||||
import io.legado.app.utils.FileUtils
|
||||
@@ -252,14 +253,17 @@ object LocalBook {
|
||||
order = appDb.bookDao.minOrder - 1
|
||||
)
|
||||
upBookInfo(book)
|
||||
book.upKind()
|
||||
appDb.bookDao.insert(book)
|
||||
} else {
|
||||
deleteBook(book, false)
|
||||
upBookInfo(book)
|
||||
book.upKind()
|
||||
// 触发 isLocalModified
|
||||
book.latestChapterTime = 0
|
||||
//已有书籍说明是更新,删除原有目录
|
||||
appDb.bookChapterDao.delByBook(bookUrl)
|
||||
appDb.bookDao.update(book)
|
||||
}
|
||||
return book
|
||||
}
|
||||
@@ -290,6 +294,7 @@ object LocalBook {
|
||||
//附加压缩包名称 以便解压文件被删后再解压
|
||||
origin = "${BookType.localTag}::${archiveFileDoc.name}"
|
||||
addType(BookType.archive)
|
||||
upKind()
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.legado.app.data.entities.TxtTocRule
|
||||
import io.legado.app.exception.EmptyFileException
|
||||
import io.legado.app.help.DefaultData
|
||||
import io.legado.app.help.book.isLocalModified
|
||||
import io.legado.app.help.book.upKind
|
||||
import io.legado.app.utils.EncodingDetect
|
||||
import io.legado.app.utils.MD5Utils
|
||||
import io.legado.app.utils.StringUtils
|
||||
@@ -94,6 +95,7 @@ class TextFile(private var book: Book) {
|
||||
}
|
||||
val (toc, wordCount) = analyze(book.tocUrl.toPattern(Pattern.MULTILINE))
|
||||
book.wordCount = StringUtils.wordCountFormat(wordCount)
|
||||
book.upKind()
|
||||
toc.forEachIndexed { index, bookChapter ->
|
||||
bookChapter.index = index
|
||||
bookChapter.bookUrl = book.bookUrl
|
||||
|
||||
@@ -36,6 +36,7 @@ import io.legado.app.help.book.isNotShelf
|
||||
import io.legado.app.help.book.isSameNameAuthor
|
||||
import io.legado.app.help.book.isWebFile
|
||||
import io.legado.app.help.book.removeType
|
||||
import io.legado.app.help.book.upKind
|
||||
import io.legado.app.help.book.updateTo
|
||||
import io.legado.app.help.config.LocalConfig
|
||||
import io.legado.app.help.coroutine.Coroutine
|
||||
@@ -51,8 +52,6 @@ import io.legado.app.model.webBook.WebBook
|
||||
import io.legado.app.ui.config.coverConfig.CoverConfig
|
||||
import io.legado.app.ui.widget.components.cover.buildCoverImageRequest
|
||||
import io.legado.app.utils.ArchiveUtils
|
||||
import io.legado.app.utils.ConvertUtils
|
||||
import io.legado.app.utils.FileDoc
|
||||
import io.legado.app.utils.GSON
|
||||
import io.legado.app.utils.ImageSaveUtils
|
||||
import io.legado.app.utils.UrlUtil
|
||||
@@ -759,24 +758,14 @@ class BookInfoViewModel(
|
||||
|
||||
private fun refreshMeta(book: Book) {
|
||||
execute {
|
||||
val fileSizePattern = Regex("""^\d[\d,.]*\s*(b|kb|M|G|T)$""", RegexOption.IGNORE_CASE)
|
||||
val kinds = (book.kind?.splitNotBlank(",", "\n").orEmpty().toList())
|
||||
.filter { !fileSizePattern.matches(it.trim()) }
|
||||
.toMutableList()
|
||||
if (book.isLocal) {
|
||||
val size = FileDoc.fromFile(book.bookUrl).size
|
||||
if (size > 0) {
|
||||
kinds.add(ConvertUtils.formatFileSize(size))
|
||||
}
|
||||
}
|
||||
val finalKinds = kinds.distinct()
|
||||
book.upKind()
|
||||
val userGroupIds = appDb.bookGroupDao.idsSum
|
||||
val groupAnd = userGroupIds and book.group
|
||||
val hasCustomGroup = book.group > 0L && groupAnd != 0L
|
||||
val groupNames = appDb.bookGroupDao.getGroupNames(book.group).joinToString(",")
|
||||
val normalizedGroupNames = groupNames.ifBlank { null }
|
||||
book.kind = finalKinds.joinToString(",")
|
||||
appDb.bookDao.update(book)
|
||||
val finalKinds = book.kind?.splitNotBlank(",", "\n").orEmpty().toList()
|
||||
Triple(finalKinds, normalizedGroupNames, hasCustomGroup)
|
||||
}.onSuccess {
|
||||
currentKindLabels = it.first
|
||||
|
||||
@@ -54,7 +54,6 @@ import io.legado.app.ui.widget.components.cover.BookshelfCover
|
||||
import io.legado.app.ui.widget.components.cover.CoilBookCover
|
||||
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import io.legado.app.utils.toTimeAgo
|
||||
|
||||
/**
|
||||
@@ -289,7 +288,7 @@ fun BookshelfItem(
|
||||
|
||||
@Composable
|
||||
fun BookGroupCover(
|
||||
books: List<BookShelfItem>,
|
||||
books: List<BookUiItem>,
|
||||
coverPath: String? = null,
|
||||
leftBottomText: String? = null,
|
||||
modifier: Modifier = Modifier
|
||||
@@ -328,7 +327,7 @@ fun BookGroupCover(
|
||||
.fillMaxHeight()
|
||||
.padding(1.dp)
|
||||
) {
|
||||
books.getOrNull(0)?.let {
|
||||
books.getOrNull(0)?.book?.let {
|
||||
CoilBookCover(
|
||||
name = it.name,
|
||||
author = it.author,
|
||||
@@ -343,7 +342,7 @@ fun BookGroupCover(
|
||||
.fillMaxHeight()
|
||||
.padding(1.dp)
|
||||
) {
|
||||
books.getOrNull(1)?.let {
|
||||
books.getOrNull(1)?.book?.let {
|
||||
CoilBookCover(
|
||||
name = it.name,
|
||||
author = it.author,
|
||||
@@ -360,7 +359,7 @@ fun BookGroupCover(
|
||||
.fillMaxHeight()
|
||||
.padding(1.dp)
|
||||
) {
|
||||
books.getOrNull(2)?.let {
|
||||
books.getOrNull(2)?.book?.let {
|
||||
CoilBookCover(
|
||||
name = it.name,
|
||||
author = it.author,
|
||||
@@ -375,7 +374,7 @@ fun BookGroupCover(
|
||||
.fillMaxHeight()
|
||||
.padding(1.dp)
|
||||
) {
|
||||
books.getOrNull(3)?.let {
|
||||
books.getOrNull(3)?.book?.let {
|
||||
CoilBookCover(
|
||||
name = it.name,
|
||||
author = it.author,
|
||||
@@ -406,7 +405,7 @@ fun BookGroupCover(
|
||||
@Composable
|
||||
fun BookGroupItemGrid(
|
||||
group: BookGroupUi,
|
||||
previewBooks: List<BookShelfItem>,
|
||||
previewBooks: List<BookUiItem>,
|
||||
countText: String? = null,
|
||||
gridStyle: Int = 0,
|
||||
titleSmallFont: Boolean = false,
|
||||
@@ -443,7 +442,7 @@ fun BookGroupItemGrid(
|
||||
@Composable
|
||||
fun BookGroupItemList(
|
||||
group: BookGroupUi,
|
||||
previewBooks: List<BookShelfItem>,
|
||||
previewBooks: List<BookUiItem>,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
countText: String? = null,
|
||||
@@ -467,7 +466,7 @@ fun BookGroupItemList(
|
||||
)
|
||||
return
|
||||
}
|
||||
val firstBookName = previewBooks.firstOrNull()?.name
|
||||
val firstBookName = previewBooks.firstOrNull()?.book?.name
|
||||
val descAnnotated = if (firstBookName != null) {
|
||||
buildAnnotatedString {
|
||||
append("最近阅读:")
|
||||
@@ -499,7 +498,7 @@ fun BookGroupItemList(
|
||||
@Composable
|
||||
fun BookGroupItemHorizontalCovers(
|
||||
group: BookGroupUi,
|
||||
previewBooks: List<BookShelfItem>,
|
||||
previewBooks: List<BookUiItem>,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
countText: String? = null,
|
||||
@@ -563,7 +562,8 @@ fun BookGroupItemHorizontalCovers(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
val coverCount = BookshelfConfig.bookshelfGroupCoverCount
|
||||
previewBooks.take(coverCount).forEach { book ->
|
||||
previewBooks.take(coverCount).forEach { bookUi ->
|
||||
val book = bookUi.book
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
@@ -597,7 +597,7 @@ fun BookGroupItemHorizontalCovers(
|
||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
@Composable
|
||||
fun BookItem(
|
||||
book: BookShelfItem,
|
||||
bookUi: BookUiItem,
|
||||
layoutMode: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
isSelected: Boolean = false,
|
||||
@@ -616,6 +616,7 @@ fun BookItem(
|
||||
onClick: () -> Unit,
|
||||
onLongClick: (() -> Unit)?
|
||||
) {
|
||||
val book = bookUi.book
|
||||
val unreadCount = book.getUnreadChapterNum()
|
||||
val unreadText = if (BookshelfConfig.showUnread && unreadCount > 0) unreadCount.toString() else null
|
||||
val bookTypeLabel = if (BookshelfConfig.showTip) {
|
||||
@@ -684,10 +685,10 @@ fun BookItem(
|
||||
desc = book.durChapterTitle ?: "",
|
||||
columnContent = if (layoutMode == 0 && !isCompact && BookshelfConfig.showBookIntro) {
|
||||
{
|
||||
val kindList = book.kind?.splitNotBlank(",", "\n")?.filter { it.isNotBlank() }
|
||||
val kindList = bookUi.displayTags
|
||||
val intro = book.intro?.takeIf { it.isNotBlank() }
|
||||
val customTagColors = if (ThemeConfig.enableCustomTagColors) ThemeConfig.getCustomTagColors() else emptyList()
|
||||
if (BookshelfConfig.bookshelfShowTag && !kindList.isNullOrEmpty()) {
|
||||
if (BookshelfConfig.bookshelfShowTag && kindList.isNotEmpty()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
@@ -3,6 +3,9 @@ package io.legado.app.ui.main.bookshelf
|
||||
import androidx.compose.runtime.Stable
|
||||
import io.legado.app.constant.BookType
|
||||
import io.legado.app.data.entities.Book
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlin.math.max
|
||||
|
||||
@Stable
|
||||
@@ -27,7 +30,8 @@ data class BookShelfItem(
|
||||
val order: Int,
|
||||
val canUpdate: Boolean = true,
|
||||
val intro: String? = null,
|
||||
val kind: String? = null
|
||||
val kind: String? = null,
|
||||
val wordCount: String? = null
|
||||
) {
|
||||
fun getDisplayCover() = if (customCoverUrl.isNullOrEmpty()) coverUrl else customCoverUrl
|
||||
|
||||
@@ -41,8 +45,41 @@ data class BookShelfItem(
|
||||
|
||||
val isNew: Boolean get() = lastCheckCount > 0
|
||||
|
||||
|
||||
fun getUnreadChapterNum() = max(totalChapterNum - durChapterIndex - 1, 0)
|
||||
|
||||
/**
|
||||
* 将 DTO 转换为专为 Compose 设计的 UI 状态
|
||||
*/
|
||||
fun toUiItem(): BookUiItem {
|
||||
val tagList = mutableListOf<String>()
|
||||
kind?.splitNotBlank(",", "\n")?.filter { it.isNotBlank() }?.let {
|
||||
tagList.addAll(it)
|
||||
}
|
||||
if (!wordCount.isNullOrBlank() && !tagList.contains(wordCount)) {
|
||||
tagList.add(wordCount)
|
||||
}
|
||||
|
||||
return BookUiItem(
|
||||
book = this,
|
||||
displayTags = tagList.toImmutableList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 理想实现:专为 UI 设计的状态类
|
||||
*/
|
||||
@Stable
|
||||
data class BookUiItem(
|
||||
val book: BookShelfItem,
|
||||
val displayTags: ImmutableList<String>
|
||||
) {
|
||||
fun matches(key: String): Boolean {
|
||||
return book.name.contains(key, true) ||
|
||||
book.author.contains(key, true) ||
|
||||
book.originName.contains(key, true) ||
|
||||
displayTags.any { it.contains(key, true) }
|
||||
}
|
||||
}
|
||||
|
||||
fun BookShelfItem.toLightBook() = Book(
|
||||
@@ -64,5 +101,7 @@ fun BookShelfItem.toLightBook() = Book(
|
||||
type = type,
|
||||
group = group,
|
||||
order = order,
|
||||
canUpdate = canUpdate
|
||||
canUpdate = canUpdate,
|
||||
wordCount = wordCount,
|
||||
kind = kind
|
||||
)
|
||||
|
||||
@@ -847,7 +847,7 @@ fun BookshelfScreen(
|
||||
uiState = uiState,
|
||||
selectedBookUrls = selectedBookUrls,
|
||||
canReorderBooks = false,
|
||||
onToggleBookSelection = { toggleBookSelection(it.bookUrl) },
|
||||
onToggleBookSelection = { toggleBookSelection(it.book.bookUrl) },
|
||||
draggingBooks = null,
|
||||
pendingSavedBooks = null,
|
||||
onDragStarted = {},
|
||||
@@ -890,7 +890,7 @@ fun BookshelfScreen(
|
||||
uiState = uiState,
|
||||
selectedBookUrls = selectedBookUrls,
|
||||
canReorderBooks = canReorderBooks,
|
||||
onToggleBookSelection = { toggleBookSelection(it.bookUrl) },
|
||||
onToggleBookSelection = { toggleBookSelection(it.book.bookUrl) },
|
||||
draggingBooks = if (isSelectedGroup) {
|
||||
uiState.draggingBooks
|
||||
} else {
|
||||
@@ -1241,15 +1241,15 @@ private data class BookshelfEditStickySummary(
|
||||
@Composable
|
||||
fun BookshelfPage(
|
||||
paddingValues: PaddingValues,
|
||||
books: ImmutableList<BookShelfItem>,
|
||||
books: ImmutableList<BookUiItem>,
|
||||
uiState: BookshelfUiState,
|
||||
selectedBookUrls: ImmutableSet<String>,
|
||||
canReorderBooks: Boolean,
|
||||
onToggleBookSelection: (BookShelfItem) -> Unit,
|
||||
draggingBooks: ImmutableList<BookShelfItem>?,
|
||||
pendingSavedBooks: ImmutableList<BookShelfItem>?,
|
||||
onDragStarted: (ImmutableList<BookShelfItem>) -> Unit,
|
||||
onMoveBook: (fromIndex: Int, toIndex: Int, currentBooks: ImmutableList<BookShelfItem>) -> Unit,
|
||||
onToggleBookSelection: (BookUiItem) -> Unit,
|
||||
draggingBooks: ImmutableList<BookUiItem>?,
|
||||
pendingSavedBooks: ImmutableList<BookUiItem>?,
|
||||
onDragStarted: (ImmutableList<BookUiItem>) -> Unit,
|
||||
onMoveBook: (fromIndex: Int, toIndex: Int, currentBooks: ImmutableList<BookUiItem>) -> Unit,
|
||||
onDragFinished: () -> Unit,
|
||||
onGlobalSearch: () -> Unit,
|
||||
onBookClick: (BookShelfItem) -> Unit,
|
||||
@@ -1367,15 +1367,15 @@ fun BookshelfPage(
|
||||
horizontalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
|
||||
showFastScroll = showFastScroll
|
||||
) {
|
||||
items(displayBooks, key = { it.bookUrl }) { book ->
|
||||
val isSelected = selectedBookUrls.contains(book.bookUrl)
|
||||
items(displayBooks, key = { it.book.bookUrl }) { bookUi ->
|
||||
val isSelected = selectedBookUrls.contains(bookUi.book.bookUrl)
|
||||
ReorderableItem(
|
||||
state = reorderableState,
|
||||
key = book.bookUrl,
|
||||
key = bookUi.book.bookUrl,
|
||||
enabled = canReorderBooks
|
||||
) {
|
||||
BookItem(
|
||||
book = book,
|
||||
bookUi = bookUi,
|
||||
modifier = Modifier.then(
|
||||
if (canReorderBooks) {
|
||||
Modifier.longPressDraggableHandle(
|
||||
@@ -1399,7 +1399,7 @@ fun BookshelfPage(
|
||||
isSelected = isSelected,
|
||||
gridStyle = bookItemGridStyle,
|
||||
isCompact = bookItemIsCompact,
|
||||
isUpdating = uiState.updatingBooks.contains(book.bookUrl),
|
||||
isUpdating = uiState.updatingBooks.contains(bookUi.book.bookUrl),
|
||||
titleSmallFont = bookItemTitleSmallFont,
|
||||
titleCenter = bookItemTitleCenter,
|
||||
titleMaxLines = bookItemTitleMaxLines,
|
||||
@@ -1408,12 +1408,12 @@ fun BookshelfPage(
|
||||
searchKey = uiState.searchKey,
|
||||
sharedTransitionScope = sharedTransitionScope,
|
||||
animatedVisibilityScope = animatedVisibilityScope,
|
||||
sharedCoverKey = if (isCurrentPage) bookCoverSharedElementKey(book.bookUrl) else null,
|
||||
sharedCoverKey = if (isCurrentPage) bookCoverSharedElementKey(bookUi.book.bookUrl) else null,
|
||||
onClick = {
|
||||
if (uiState.isEditMode) {
|
||||
onToggleBookSelection(book)
|
||||
onToggleBookSelection(bookUi)
|
||||
} else {
|
||||
onBookClick(book)
|
||||
onBookClick(bookUi.book)
|
||||
}
|
||||
},
|
||||
onLongClick = if (canReorderBooks) {
|
||||
@@ -1421,9 +1421,9 @@ fun BookshelfPage(
|
||||
} else {
|
||||
{
|
||||
if (uiState.isEditMode) {
|
||||
onToggleBookSelection(book)
|
||||
onToggleBookSelection(bookUi)
|
||||
} else {
|
||||
onBookLongClick(book)
|
||||
onBookLongClick(bookUi.book)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ sealed interface BookshelfOverlay {
|
||||
|
||||
@Stable
|
||||
data class BookshelfUiState(
|
||||
override val items: ImmutableList<BookShelfItem> = persistentListOf(),
|
||||
override val items: ImmutableList<BookUiItem> = persistentListOf(),
|
||||
override val selectedIds: ImmutableSet<Any> = persistentSetOf(),
|
||||
override val searchKey: String = "",
|
||||
override val isSearch: Boolean = false,
|
||||
@@ -41,7 +41,7 @@ data class BookshelfUiState(
|
||||
val isInitialLoading: Boolean = true,
|
||||
val groups: ImmutableList<BookGroupUi> = persistentListOf(),
|
||||
val allGroups: ImmutableList<BookGroupUi> = persistentListOf(),
|
||||
val groupPreviews: ImmutableMap<Long, ImmutableList<BookShelfItem>> = persistentMapOf(),
|
||||
val groupPreviews: ImmutableMap<Long, ImmutableList<BookUiItem>> = persistentMapOf(),
|
||||
val groupBookCounts: ImmutableMap<Long, Int> = persistentMapOf(),
|
||||
val currentGroupBookCount: Int = 0,
|
||||
val allBooksCount: Int = 0,
|
||||
@@ -61,7 +61,7 @@ data class BookshelfUiState(
|
||||
val title: String = "",
|
||||
val subtitle: String? = null,
|
||||
val currentGroupName: String? = null,
|
||||
val draggingBooks: ImmutableList<BookShelfItem>? = null,
|
||||
val pendingSavedBooks: ImmutableList<BookShelfItem>? = null,
|
||||
val allGroupBooks: ImmutableMap<Long, ImmutableList<BookShelfItem>> = persistentMapOf()
|
||||
) : ListUiState<BookShelfItem>
|
||||
val draggingBooks: ImmutableList<BookUiItem>? = null,
|
||||
val pendingSavedBooks: ImmutableList<BookUiItem>? = null,
|
||||
val allGroupBooks: ImmutableMap<Long, ImmutableList<BookUiItem>> = persistentMapOf()
|
||||
) : ListUiState<BookUiItem>
|
||||
|
||||
@@ -117,8 +117,8 @@ class BookshelfViewModel(
|
||||
private val isInFolderRootFlow = MutableStateFlow(BookshelfConfig.bookGroupStyle == 2)
|
||||
private val isRefreshingFlow = MutableStateFlow(false)
|
||||
private val bookGroupStyleFlow = MutableStateFlow(BookshelfConfig.bookGroupStyle)
|
||||
private val draggingBooksFlow = MutableStateFlow<List<BookShelfItem>?>(null)
|
||||
private val pendingSavedBooksFlow = MutableStateFlow<List<BookShelfItem>?>(null)
|
||||
private val draggingBooksFlow = MutableStateFlow<List<BookUiItem>?>(null)
|
||||
private val pendingSavedBooksFlow = MutableStateFlow<List<BookUiItem>?>(null)
|
||||
private val isInitialLoadingFlow = MutableStateFlow(true)
|
||||
|
||||
private data class BookshelfSortConfig(
|
||||
@@ -170,7 +170,7 @@ class BookshelfViewModel(
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
||||
|
||||
private data class GroupPreviewState(
|
||||
val previews: ImmutableMap<Long, ImmutableList<BookShelfItem>>,
|
||||
val previews: ImmutableMap<Long, ImmutableList<BookUiItem>>,
|
||||
val counts: ImmutableMap<Long, Int>,
|
||||
val allBookCount: Int
|
||||
)
|
||||
@@ -189,7 +189,7 @@ class BookshelfViewModel(
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), BookshelfGroupSelectorState())
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val booksFlow = groupIdFlow
|
||||
val booksFlow: Flow<List<BookUiItem>> = groupIdFlow
|
||||
.flatMapLatest { groupId ->
|
||||
combine(
|
||||
appDb.bookDao.flowBookShelfByGroup(groupId),
|
||||
@@ -200,12 +200,12 @@ class BookshelfViewModel(
|
||||
list,
|
||||
groups.find { it.groupId == groupId },
|
||||
sortConfig
|
||||
)
|
||||
).map { it.toUiItem() }
|
||||
}
|
||||
}.distinctUntilChanged().flowOn(Dispatchers.Default)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val allGroupBooksFlow: StateFlow<Map<Long, List<BookShelfItem>>> = combine(
|
||||
private val allGroupBooksFlow: StateFlow<Map<Long, List<BookUiItem>>> = combine(
|
||||
groupsFlow, sortConfigFlow
|
||||
) { groups, sortConfig ->
|
||||
groups to sortConfig
|
||||
@@ -215,7 +215,7 @@ class BookshelfViewModel(
|
||||
} else {
|
||||
val flows = groups.map { group ->
|
||||
appDb.bookDao.flowBookShelfByGroup(group.groupId).map { books ->
|
||||
group.groupId to sortBooks(books, group, sortConfig)
|
||||
group.groupId to sortBooks(books, group, sortConfig).map { it.toUiItem() }
|
||||
}
|
||||
}
|
||||
combine(flows) { it.toMap() }
|
||||
@@ -224,13 +224,13 @@ class BookshelfViewModel(
|
||||
.flowOn(Dispatchers.Default)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap())
|
||||
|
||||
private val allGroupBooksImmutableFlow: StateFlow<ImmutableMap<Long, ImmutableList<BookShelfItem>>> =
|
||||
private val allGroupBooksImmutableFlow: StateFlow<ImmutableMap<Long, ImmutableList<BookUiItem>>> =
|
||||
allGroupBooksFlow.map { map ->
|
||||
map.mapValues { it.value.toImmutableList() }.toImmutableMap()
|
||||
}.flowOn(Dispatchers.Default)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), persistentMapOf())
|
||||
|
||||
private val visibleBooksFlow = combine(
|
||||
private val visibleBooksFlow: Flow<List<BookUiItem>> = combine(
|
||||
booksFlow,
|
||||
searchKeyFlow,
|
||||
searchModeFlow
|
||||
@@ -254,7 +254,7 @@ class BookshelfViewModel(
|
||||
selectedBookUrlsFlow,
|
||||
visibleBooksFlow
|
||||
) { selectedBookUrls, visibleBooks ->
|
||||
val visibleBookUrls = visibleBooks.mapTo(hashSetOf()) { it.bookUrl }
|
||||
val visibleBookUrls = visibleBooks.mapTo(hashSetOf()) { it.book.bookUrl }
|
||||
selectedBookUrls.intersect(visibleBookUrls)
|
||||
}.distinctUntilChanged()
|
||||
|
||||
@@ -281,11 +281,11 @@ class BookshelfViewModel(
|
||||
}
|
||||
val previewFlow = appDb.bookDao.flowGroupPreview(group.groupId)
|
||||
combine(countFlow, previewFlow) { count, preview ->
|
||||
Triple(group.groupId, count, preview)
|
||||
Triple(group.groupId, count, preview.map { it.toUiItem() })
|
||||
}
|
||||
}
|
||||
combine(groupFlows) { results ->
|
||||
var previews = persistentMapOf<Long, ImmutableList<BookShelfItem>>()
|
||||
var previews = persistentMapOf<Long, ImmutableList<BookUiItem>>()
|
||||
var counts = persistentMapOf<Long, Int>()
|
||||
var allBookCount = 0
|
||||
results.forEach { (groupId, count, preview) ->
|
||||
@@ -344,8 +344,8 @@ class BookshelfViewModel(
|
||||
val isInFolderRoot: Boolean,
|
||||
val isRefreshing: Boolean,
|
||||
val bookGroupStyle: Int,
|
||||
val draggingBooks: List<BookShelfItem>?,
|
||||
val pendingSavedBooks: List<BookShelfItem>?
|
||||
val draggingBooks: List<BookUiItem>?,
|
||||
val pendingSavedBooks: List<BookUiItem>?
|
||||
)
|
||||
|
||||
private val editStateFlow = combine(
|
||||
@@ -387,7 +387,8 @@ class BookshelfViewModel(
|
||||
GroupPreviewState(persistentMapOf(), persistentMapOf(), 0)
|
||||
)
|
||||
|
||||
private val allGroupBooksStateFlow = MutableStateFlow<ImmutableMap<Long, ImmutableList<BookShelfItem>>>(
|
||||
private val allGroupBooksStateFlow =
|
||||
MutableStateFlow<ImmutableMap<Long, ImmutableList<BookUiItem>>>(
|
||||
persistentMapOf()
|
||||
)
|
||||
|
||||
@@ -411,7 +412,7 @@ class BookshelfViewModel(
|
||||
}
|
||||
|
||||
private data class BookshelfDataCore(
|
||||
val books: List<BookShelfItem>,
|
||||
val books: List<BookUiItem>,
|
||||
val groups: List<BookGroup>,
|
||||
val allGroups: List<BookGroup>,
|
||||
val previews: GroupPreviewState,
|
||||
@@ -419,12 +420,12 @@ class BookshelfViewModel(
|
||||
)
|
||||
|
||||
private data class BookshelfDataState(
|
||||
val books: List<BookShelfItem>,
|
||||
val books: List<BookUiItem>,
|
||||
val groups: List<BookGroupUi>,
|
||||
val allGroups: List<BookGroupUi>,
|
||||
val previews: GroupPreviewState,
|
||||
val internal: InternalState,
|
||||
val allGroupBooks: ImmutableMap<Long, ImmutableList<BookShelfItem>>
|
||||
val allGroupBooks: ImmutableMap<Long, ImmutableList<BookUiItem>>
|
||||
)
|
||||
|
||||
val uiState: StateFlow<BookshelfUiState> = combine(
|
||||
@@ -540,14 +541,14 @@ class BookshelfViewModel(
|
||||
}
|
||||
|
||||
private fun filterBooks(
|
||||
books: List<BookShelfItem>,
|
||||
books: List<BookUiItem>,
|
||||
searchKey: String,
|
||||
isSearchMode: Boolean
|
||||
): List<BookShelfItem> {
|
||||
): List<BookUiItem> {
|
||||
return if (!isSearchMode || searchKey.isBlank()) {
|
||||
books
|
||||
} else {
|
||||
books.filter { it.matchesSearchKey(searchKey) }
|
||||
books.filter { it.matches(searchKey) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,11 +674,11 @@ class BookshelfViewModel(
|
||||
}
|
||||
|
||||
fun selectAllVisible() {
|
||||
selectedBookUrlsFlow.value = uiState.value.items.mapTo(hashSetOf()) { it.bookUrl }
|
||||
selectedBookUrlsFlow.value = uiState.value.items.mapTo(hashSetOf()) { it.book.bookUrl }
|
||||
}
|
||||
|
||||
fun invertVisibleSelection() {
|
||||
val visibleBookUrls = uiState.value.items.mapTo(hashSetOf()) { it.bookUrl }
|
||||
val visibleBookUrls = uiState.value.items.mapTo(hashSetOf()) { it.book.bookUrl }
|
||||
selectedBookUrlsFlow.value = visibleBookUrls - selectedBookUrlsFlow.value
|
||||
}
|
||||
|
||||
@@ -719,13 +720,13 @@ class BookshelfViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun saveBookOrder(reorderedBooks: List<BookShelfItem>) {
|
||||
fun saveBookOrder(reorderedBooks: List<BookUiItem>) {
|
||||
if (reorderedBooks.isEmpty()) return
|
||||
val isDescending = BookshelfConfig.bookshelfSortOrder == 1
|
||||
val maxOrder = reorderedBooks.size
|
||||
execute {
|
||||
val updates = reorderedBooks.mapIndexedNotNull { index, book ->
|
||||
appDb.bookDao.getBook(book.bookUrl)?.apply {
|
||||
val updates = reorderedBooks.mapIndexedNotNull { index, bookUi ->
|
||||
appDb.bookDao.getBook(bookUi.book.bookUrl)?.apply {
|
||||
order = if (isDescending) maxOrder - index else index + 1
|
||||
}
|
||||
}
|
||||
@@ -756,19 +757,19 @@ class BookshelfViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshBooks(books: List<BookShelfItem>) {
|
||||
fun refreshBooks(books: List<BookUiItem>) {
|
||||
if (isRefreshingFlow.value) return
|
||||
isRefreshingFlow.value = true
|
||||
val limit = BookshelfConfig.bookshelfRefreshingLimit
|
||||
val list = if (limit > 0) books.take(limit) else books
|
||||
enqueueTocUpdate(list, resetRefreshWhenIdle = true)
|
||||
enqueueTocUpdate(list.map { it.book }, resetRefreshWhenIdle = true)
|
||||
}
|
||||
|
||||
fun startDraggingBooks(books: List<BookShelfItem>) {
|
||||
fun startDraggingBooks(books: List<BookUiItem>) {
|
||||
draggingBooksFlow.value = books
|
||||
}
|
||||
|
||||
fun moveDraggingBook(fromIndex: Int, toIndex: Int, fallbackBooks: List<BookShelfItem>) {
|
||||
fun moveDraggingBook(fromIndex: Int, toIndex: Int, fallbackBooks: List<BookUiItem>) {
|
||||
if (fromIndex == toIndex) return
|
||||
val sourceBooks = draggingBooksFlow.value ?: fallbackBooks
|
||||
if (fromIndex !in sourceBooks.indices || toIndex !in sourceBooks.indices) return
|
||||
@@ -778,19 +779,19 @@ class BookshelfViewModel(
|
||||
}
|
||||
|
||||
fun finishDraggingBooks() {
|
||||
val reorderedBooks = draggingBooksFlow.value ?: return
|
||||
pendingSavedBooksFlow.value = reorderedBooks
|
||||
val reorderedUiBooks = draggingBooksFlow.value ?: return
|
||||
pendingSavedBooksFlow.value = reorderedUiBooks
|
||||
draggingBooksFlow.value = null
|
||||
saveBookOrder(reorderedBooks)
|
||||
saveBookOrder(reorderedUiBooks)
|
||||
}
|
||||
|
||||
private fun syncDragState(books: List<BookShelfItem>, canReorderBooks: Boolean) {
|
||||
private fun syncDragState(books: List<BookUiItem>, canReorderBooks: Boolean) {
|
||||
if (!canReorderBooks) {
|
||||
clearDragState()
|
||||
return
|
||||
}
|
||||
val pending = pendingSavedBooksFlow.value ?: return
|
||||
if (books.map { it.bookUrl } == pending.map { it.bookUrl }) {
|
||||
if (books.map { it.book.bookUrl } == pending.map { it.book.bookUrl }) {
|
||||
pendingSavedBooksFlow.value = null
|
||||
}
|
||||
}
|
||||
@@ -811,10 +812,10 @@ class BookshelfViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun upToc(books: List<BookShelfItem>) {
|
||||
fun upToc(books: List<BookUiItem>) {
|
||||
val limit = BookshelfConfig.bookshelfRefreshingLimit
|
||||
val list = if (limit > 0) books.take(limit) else books
|
||||
enqueueTocUpdate(list, resetRefreshWhenIdle = false)
|
||||
enqueueTocUpdate(list.map { it.book }, resetRefreshWhenIdle = false)
|
||||
}
|
||||
|
||||
private fun enqueueTocUpdate(
|
||||
@@ -1088,7 +1089,7 @@ class BookshelfViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun exportToUri(uri: Uri, items: List<BookShelfItem>) {
|
||||
fun exportToUri(uri: Uri, items: List<BookUiItem>) {
|
||||
execute {
|
||||
context.contentResolver.openOutputStream(uri)?.use { out ->
|
||||
val writer = JsonWriter(OutputStreamWriter(out, "UTF-8"))
|
||||
@@ -1096,11 +1097,11 @@ class BookshelfViewModel(
|
||||
writer.beginArray()
|
||||
items.forEach {
|
||||
val bookMap = hashMapOf<String, String?>()
|
||||
bookMap["name"] = it.name
|
||||
bookMap["author"] = it.author
|
||||
bookMap["name"] = it.book.name
|
||||
bookMap["author"] = it.book.author
|
||||
// intro is not in BookShelfItem, fetch from DB if needed or skip
|
||||
// For now, let's keep it simple and skip intro or fetch it
|
||||
val fullBook = appDb.bookDao.getBook(it.bookUrl)
|
||||
val fullBook = appDb.bookDao.getBook(it.book.bookUrl)
|
||||
bookMap["intro"] = fullBook?.getDisplayIntro()
|
||||
GSON.toJson(bookMap, bookMap::class.java, writer)
|
||||
}
|
||||
@@ -1114,14 +1115,14 @@ class BookshelfViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun uploadBookshelf(items: List<BookShelfItem>) {
|
||||
fun uploadBookshelf(items: List<BookUiItem>) {
|
||||
execute {
|
||||
val json = withContext(Dispatchers.Default) {
|
||||
val list = items.map {
|
||||
val bookMap = hashMapOf<String, String?>()
|
||||
bookMap["name"] = it.name
|
||||
bookMap["author"] = it.author
|
||||
val fullBook = appDb.bookDao.getBook(it.bookUrl)
|
||||
bookMap["name"] = it.book.name
|
||||
bookMap["author"] = it.book.author
|
||||
val fullBook = appDb.bookDao.getBook(it.book.bookUrl)
|
||||
bookMap["intro"] = fullBook?.getDisplayIntro()
|
||||
bookMap
|
||||
}
|
||||
@@ -1149,7 +1150,7 @@ class BookshelfViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun exportBookshelf(items: List<BookShelfItem>?, success: (file: File) -> Unit) {
|
||||
fun exportBookshelf(items: List<BookUiItem>?, success: (file: File) -> Unit) {
|
||||
execute {
|
||||
items?.let {
|
||||
val path = "${context.filesDir}/books.json"
|
||||
@@ -1161,9 +1162,9 @@ class BookshelfViewModel(
|
||||
writer.beginArray()
|
||||
items.forEach {
|
||||
val bookMap = hashMapOf<String, String?>()
|
||||
bookMap["name"] = it.name
|
||||
bookMap["author"] = it.author
|
||||
val fullBook = appDb.bookDao.getBook(it.bookUrl)
|
||||
bookMap["name"] = it.book.name
|
||||
bookMap["author"] = it.book.author
|
||||
val fullBook = appDb.bookDao.getBook(it.book.bookUrl)
|
||||
bookMap["intro"] = fullBook?.getDisplayIntro()
|
||||
GSON.toJson(bookMap, bookMap::class.java, writer)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user