UI/TTS improvements
This commit is contained in:
1 parent
0eba07b594
commit
ed66ea0d55
13 files changed
+250
-57
No files matched your search
@@ -702,21 +702,14 @@ private class JdbcBookFileRepository(private val connection: Connection) : BookF
|
||||
FROM book_files f
|
||||
LEFT JOIN books b ON b.id = f.book_id
|
||||
WHERE f.duplicate_of_file_id IS NULL
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN f.reading_status = 'READING' THEN 0
|
||||
WHEN f.reading_status = 'NOT_INTERESTED' THEN 2
|
||||
ELSE 1
|
||||
END,
|
||||
CASE WHEN f.reading_status = 'READING' THEN f.last_read_at END DESC NULLS LAST,
|
||||
LOWER(COALESCE(NULLIF(b.title, ''), NULLIF(f.original_filename, ''), f.id)),
|
||||
f.id
|
||||
LIMIT ? OFFSET ?
|
||||
""".trimIndent()
|
||||
).use { statement ->
|
||||
statement.setInt(1, limit)
|
||||
statement.setInt(2, offset)
|
||||
statement.executeQuery().use { resultSet -> resultSet.mapRows { it.toLibraryFileRecord() } }
|
||||
statement.executeQuery().use { resultSet ->
|
||||
resultSet.mapRows { it.toLibraryFileRecord() }
|
||||
.sortedWith(LibraryFileNaturalTitleComparator)
|
||||
.drop(offset)
|
||||
.take(limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1097,6 +1090,50 @@ private fun String?.matchPositions(prefixes: List<String>, fieldOffset: Int): Li
|
||||
|
||||
private val SearchWordRegex = Regex("""[\p{L}\p{N}]+""")
|
||||
|
||||
private val LibraryFileNaturalTitleComparator = Comparator<LibraryFileRecord> { left, right ->
|
||||
val titleCompare = naturalCompare(left.sortTitle(), right.sortTitle())
|
||||
if (titleCompare != 0) titleCompare else left.fileId.compareTo(right.fileId)
|
||||
}
|
||||
|
||||
private fun LibraryFileRecord.sortTitle(): String =
|
||||
title?.takeIf(String::isNotBlank)
|
||||
?: originalFilename?.takeIf(String::isNotBlank)
|
||||
?: fileId
|
||||
|
||||
private fun naturalCompare(left: String, right: String): Int {
|
||||
var leftIndex = 0
|
||||
var rightIndex = 0
|
||||
var exactFallback = 0
|
||||
while (leftIndex < left.length && rightIndex < right.length) {
|
||||
val leftChar = left[leftIndex]
|
||||
val rightChar = right[rightIndex]
|
||||
if (leftChar.isDigit() && rightChar.isDigit()) {
|
||||
val leftStart = leftIndex
|
||||
val rightStart = rightIndex
|
||||
while (leftIndex < left.length && left[leftIndex].isDigit()) leftIndex += 1
|
||||
while (rightIndex < right.length && right[rightIndex].isDigit()) rightIndex += 1
|
||||
|
||||
val leftDigits = left.substring(leftStart, leftIndex).trimStart('0')
|
||||
val rightDigits = right.substring(rightStart, rightIndex).trimStart('0')
|
||||
val leftNumber = leftDigits.ifEmpty { "0" }
|
||||
val rightNumber = rightDigits.ifEmpty { "0" }
|
||||
val lengthCompare = leftNumber.length.compareTo(rightNumber.length)
|
||||
if (lengthCompare != 0) return lengthCompare
|
||||
val numberCompare = leftNumber.compareTo(rightNumber)
|
||||
if (numberCompare != 0) return numberCompare
|
||||
val rawLengthCompare = (leftIndex - leftStart).compareTo(rightIndex - rightStart)
|
||||
if (rawLengthCompare != 0) return rawLengthCompare
|
||||
} else {
|
||||
val foldedCompare = leftChar.lowercaseChar().compareTo(rightChar.lowercaseChar())
|
||||
if (foldedCompare != 0) return foldedCompare
|
||||
if (exactFallback == 0) exactFallback = leftChar.compareTo(rightChar)
|
||||
leftIndex += 1
|
||||
rightIndex += 1
|
||||
}
|
||||
}
|
||||
return left.length.compareTo(right.length).takeIf { it != 0 } ?: exactFallback
|
||||
}
|
||||
|
||||
private fun ResultSet.toLibraryFileRecord() = LibraryFileRecord(
|
||||
fileId = getString("file_id"),
|
||||
bookId = getString("book_id"),
|
||||
|
||||
@@ -161,17 +161,16 @@ class H2LibraryDatabaseTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listsReadingBooksFirstNotInterestedLastThenSortsByTitle() {
|
||||
val db = H2LibraryDatabase.openMemory("listsReadingBooksFirstNotInterestedLastThenSortsByTitle")
|
||||
fun listsLibraryFilesByNaturalTitle() {
|
||||
val db = H2LibraryDatabase.openMemory("listsLibraryFilesByNaturalTitle")
|
||||
val now = 1_700_000_000_000L
|
||||
|
||||
db.transaction {
|
||||
listOf(
|
||||
Triple("book-beta", "Beta", BookReadingStatus.NEW),
|
||||
Triple("book-10", "Book 10", BookReadingStatus.READING),
|
||||
Triple("book-alpha", "Alpha", BookReadingStatus.READ),
|
||||
Triple("book-gamma", "Gamma", BookReadingStatus.READING),
|
||||
Triple("book-aardvark", "Aardvark", BookReadingStatus.READING),
|
||||
Triple("book-omega", "Omega", BookReadingStatus.NOT_INTERESTED),
|
||||
Triple("book-2", "Book 2", BookReadingStatus.NEW),
|
||||
Triple("book-beta", "Beta", BookReadingStatus.NOT_INTERESTED),
|
||||
).forEachIndexed { index, (bookId, title, status) ->
|
||||
books.upsert(
|
||||
BookRecord(
|
||||
@@ -189,7 +188,7 @@ class H2LibraryDatabaseTest {
|
||||
originalFilename = "$title.fb2",
|
||||
storageKind = BookFileStorageKind.EXTERNAL_URI,
|
||||
readingStatus = status,
|
||||
lastReadAt = if (title == "Aardvark") now + 500 else now + 100,
|
||||
lastReadAt = now + index,
|
||||
createdAt = now + index,
|
||||
updatedAt = now + index,
|
||||
)
|
||||
@@ -198,7 +197,7 @@ class H2LibraryDatabaseTest {
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
listOf("Aardvark", "Gamma", "Alpha", "Beta", "Omega"),
|
||||
listOf("Alpha", "Beta", "Book 2", "Book 10"),
|
||||
db.files.listLibraryFiles().map { it.title },
|
||||
)
|
||||
db.close()
|
||||
|
||||
Reference in new issue
Block a user