1.2 Beta last state !!!
This commit is contained in:
@@ -40,6 +40,13 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import inhale.rip.epook.data.Book
|
import inhale.rip.epook.data.Book
|
||||||
import nl.siegmann.epublib.domain.Book as EpubBook
|
import nl.siegmann.epublib.domain.Book as EpubBook
|
||||||
|
|
||||||
|
// Move the Chapter data class outside the composable
|
||||||
|
private data class Chapter(
|
||||||
|
val index: Int,
|
||||||
|
val title: String,
|
||||||
|
val resource: Resource
|
||||||
|
)
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ReaderScreen(
|
fun ReaderScreen(
|
||||||
@@ -53,7 +60,7 @@ fun ReaderScreen(
|
|||||||
var currentSettings by remember { mutableStateOf(Settings()) }
|
var currentSettings by remember { mutableStateOf(Settings()) }
|
||||||
|
|
||||||
var currentChapterIndex by remember { mutableStateOf(0) }
|
var currentChapterIndex by remember { mutableStateOf(0) }
|
||||||
var chapters by remember { mutableStateOf(listOf<Resource>()) }
|
var chapters by remember { mutableStateOf<List<Chapter>>(emptyList()) }
|
||||||
var book by remember { mutableStateOf<EpubBook?>(null) }
|
var book by remember { mutableStateOf<EpubBook?>(null) }
|
||||||
|
|
||||||
var showControls by remember { mutableStateOf(true) }
|
var showControls by remember { mutableStateOf(true) }
|
||||||
@@ -62,6 +69,9 @@ fun ReaderScreen(
|
|||||||
val backgroundColor = MaterialTheme.colorScheme.background.toArgb()
|
val backgroundColor = MaterialTheme.colorScheme.background.toArgb()
|
||||||
val textColor = MaterialTheme.colorScheme.onBackground.toArgb()
|
val textColor = MaterialTheme.colorScheme.onBackground.toArgb()
|
||||||
|
|
||||||
|
// Add state for reading position
|
||||||
|
var currentReadingPosition by remember { mutableStateOf(0) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
try {
|
try {
|
||||||
currentSettings = settingsStore.getSettings()
|
currentSettings = settingsStore.getSettings()
|
||||||
@@ -72,17 +82,62 @@ fun ReaderScreen(
|
|||||||
|
|
||||||
LaunchedEffect(bookId) {
|
LaunchedEffect(bookId) {
|
||||||
try {
|
try {
|
||||||
|
currentChapterIndex = bookStore.getReadingPosition(bookId).first()
|
||||||
val bookPath = bookStore.getBookPath(bookId)
|
val bookPath = bookStore.getBookPath(bookId)
|
||||||
val epubBook = EpubReader().readEpub(FileInputStream(bookPath))
|
val epubBook = EpubReader().readEpub(FileInputStream(bookPath))
|
||||||
book = epubBook
|
book = epubBook
|
||||||
chapters = epubBook.spine.spineReferences.mapNotNull { spineRef: nl.siegmann.epublib.domain.SpineReference ->
|
|
||||||
spineRef.resource
|
// Extract chapters with titles
|
||||||
|
chapters = epubBook.spine.spineReferences.mapIndexed { index, spineRef ->
|
||||||
|
val resource = spineRef.resource
|
||||||
|
val title = when {
|
||||||
|
// Try to get title from TOC
|
||||||
|
epubBook.tableOfContents.allUniqueResources.contains(resource) -> {
|
||||||
|
epubBook.tableOfContents.allUniqueResources
|
||||||
|
.find { it == resource }
|
||||||
|
?.title ?: "Chapter ${index + 1}"
|
||||||
|
}
|
||||||
|
// Try to parse HTML for title
|
||||||
|
resource.mediaType.toString().contains("html") -> {
|
||||||
|
try {
|
||||||
|
val html = String(resource.data, Charset.defaultCharset())
|
||||||
|
val doc = Jsoup.parse(html)
|
||||||
|
doc.select("h1, h2, h3, title").firstOrNull()?.text()
|
||||||
|
?: "Chapter ${index + 1}"
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"Chapter ${index + 1}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> "Chapter ${index + 1}"
|
||||||
|
}
|
||||||
|
Chapter(index, title, resource)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "Error loading book")
|
Timber.e(e, "Error loading book")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save position when chapter changes
|
||||||
|
LaunchedEffect(currentChapterIndex) {
|
||||||
|
try {
|
||||||
|
bookStore.updateReadingPosition(bookId, currentChapterIndex)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Error saving reading position")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle back button to save position before leaving
|
||||||
|
BackHandler {
|
||||||
|
scope.launch {
|
||||||
|
try {
|
||||||
|
bookStore.updateReadingPosition(bookId, currentChapterIndex)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Error saving reading position on exit")
|
||||||
|
}
|
||||||
|
onNavigateBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
@@ -149,7 +204,8 @@ fun ReaderScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
update = { webView ->
|
update = { webView ->
|
||||||
val chapter = chapters[currentChapterIndex]
|
val chapter = chapters.getOrNull(currentChapterIndex)?.resource
|
||||||
|
chapter?.let { resource ->
|
||||||
val html = """
|
val html = """
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
@@ -166,12 +222,13 @@ fun ReaderScreen(
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
${String(chapter.data, Charset.defaultCharset())}
|
${String(resource.data, Charset.defaultCharset())}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null)
|
webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,31 +281,33 @@ fun ReaderScreen(
|
|||||||
if (showChapterList) {
|
if (showChapterList) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = { showChapterList = false },
|
onDismissRequest = { showChapterList = false },
|
||||||
title = { Text("Chapters") },
|
title = { Text("Table of Contents") },
|
||||||
text = {
|
text = {
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
items(
|
items(
|
||||||
items = List(chapters.size) { it },
|
items = chapters,
|
||||||
key = { it }
|
key = { chapter: Chapter -> chapter.index }
|
||||||
) { index ->
|
) { chapter ->
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text("Chapter ${index + 1}")
|
Text(
|
||||||
|
text = chapter.title,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
},
|
},
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
currentChapterIndex = index
|
currentChapterIndex = chapter.index
|
||||||
showChapterList = false
|
showChapterList = false
|
||||||
},
|
},
|
||||||
leadingContent = if (currentChapterIndex == index) {
|
leadingContent = {
|
||||||
{
|
if (currentChapterIndex == chapter.index) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.RadioButtonChecked,
|
Icons.Default.RadioButtonChecked,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = MaterialTheme.colorScheme.primary
|
tint = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
{
|
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.RadioButtonUnchecked,
|
Icons.Default.RadioButtonUnchecked,
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import android.util.Log
|
|||||||
import nl.siegmann.epublib.domain.Book as EpubBook
|
import nl.siegmann.epublib.domain.Book as EpubBook
|
||||||
import nl.siegmann.epublib.epub.EpubReader
|
import nl.siegmann.epublib.epub.EpubReader
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "books")
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "books")
|
||||||
|
|
||||||
@@ -35,13 +36,28 @@ class BookStore(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateReadingPosition(bookId: String, position: Int) {
|
suspend fun updateReadingPosition(bookId: String, position: Int) {
|
||||||
|
try {
|
||||||
bookDao.updateReadingPosition(bookId, position)
|
bookDao.updateReadingPosition(bookId, position)
|
||||||
|
Timber.d("Updated reading position for book $bookId to $position")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Failed to update reading position")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getReadingPosition(bookId: String): Flow<Int> {
|
fun getReadingPosition(bookId: String): Flow<Int> {
|
||||||
return bookDao.getBookPosition(bookId)
|
return bookDao.getBookPosition(bookId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getInitialReadingPosition(bookId: String): Int {
|
||||||
|
return try {
|
||||||
|
bookDao.getBook(bookId)?.readingPosition ?: 0
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Failed to get initial reading position")
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun deleteBook(bookId: String) {
|
suspend fun deleteBook(bookId: String) {
|
||||||
Log.e(TAG, "🔥 DELETE BOOK STARTED 🔥")
|
Log.e(TAG, "🔥 DELETE BOOK STARTED 🔥")
|
||||||
Log.e(TAG, "Attempting to delete book with ID: $bookId")
|
Log.e(TAG, "Attempting to delete book with ID: $bookId")
|
||||||
|
|||||||
Reference in New Issue
Block a user