🚀 Nested Chapters in Reader
* Fixed issue with incorrect navigation * Added Nested Chapters, e.g. Book -> Chapter Resolves: #130
This commit is contained in:
parent
804c7d24a9
commit
cd18c430a5
12 changed files with 215 additions and 63 deletions
|
|
@ -164,7 +164,8 @@ class DocumentParser @Inject constructor(
|
||||||
) {
|
) {
|
||||||
readerText.add(
|
readerText.add(
|
||||||
0, ReaderText.Chapter(
|
0, ReaderText.Chapter(
|
||||||
title = formattedLine.clearAllMarkdown()
|
title = formattedLine.clearAllMarkdown(),
|
||||||
|
nested = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
chapterAdded = true
|
chapterAdded = true
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import javax.inject.Inject
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
private const val EPUB_TAG = "EPUB Parser"
|
private const val EPUB_TAG = "EPUB Parser"
|
||||||
private typealias Title = String
|
private typealias Source = String
|
||||||
|
|
||||||
private val dispatcher = Dispatchers.IO.limitedParallelism(3)
|
private val dispatcher = Dispatchers.IO.limitedParallelism(3)
|
||||||
|
|
||||||
|
|
@ -108,7 +108,7 @@ class EpubTextParser @Inject constructor(
|
||||||
private suspend fun ZipFile.parseEpub(
|
private suspend fun ZipFile.parseEpub(
|
||||||
chapterEntries: List<ZipEntry>,
|
chapterEntries: List<ZipEntry>,
|
||||||
imageEntries: List<ZipEntry>,
|
imageEntries: List<ZipEntry>,
|
||||||
chapterTitleEntries: Map<Title, List<String>>?
|
chapterTitleEntries: Map<Source, ReaderText.Chapter>?
|
||||||
): List<ReaderText> {
|
): List<ReaderText> {
|
||||||
|
|
||||||
val readerText = mutableListOf<ReaderText>()
|
val readerText = mutableListOf<ReaderText>()
|
||||||
|
|
@ -159,7 +159,7 @@ class EpubTextParser @Inject constructor(
|
||||||
index: Int,
|
index: Int,
|
||||||
entry: ZipEntry,
|
entry: ZipEntry,
|
||||||
imageEntries: List<ZipEntry>,
|
imageEntries: List<ZipEntry>,
|
||||||
chapterTitleMap: Map<Title, List<String>>?
|
chapterTitleMap: Map<Source, ReaderText.Chapter>?
|
||||||
) {
|
) {
|
||||||
// Getting all text
|
// Getting all text
|
||||||
val content = zip.getInputStream(entry).bufferedReader().use { it.readText() }
|
val content = zip.getInputStream(entry).bufferedReader().use { it.readText() }
|
||||||
|
|
@ -176,22 +176,24 @@ class EpubTextParser @Inject constructor(
|
||||||
chapterSource = entry.name,
|
chapterSource = entry.name,
|
||||||
chapterTitleMap = chapterTitleMap
|
chapterTitleMap = chapterTitleMap
|
||||||
).apply {
|
).apply {
|
||||||
val chapterTitle = this ?: run {
|
val chapter = this ?: run {
|
||||||
val firstVisibleText = readerText.firstOrNull { line ->
|
val firstVisibleText = readerText.firstOrNull { line ->
|
||||||
line is ReaderText.Text && line.line.text.containsVisibleText()
|
line is ReaderText.Text && line.line.text.containsVisibleText()
|
||||||
} as? ReaderText.Text ?: return
|
} as? ReaderText.Text ?: return
|
||||||
firstVisibleText.line.text
|
|
||||||
|
return@run ReaderText.Chapter(
|
||||||
|
title = firstVisibleText.line.text,
|
||||||
|
nested = false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
readerText = readerText.dropWhile { line ->
|
readerText = readerText.dropWhile { line ->
|
||||||
(line is ReaderText.Text && line.line.text.lowercase() == chapterTitle.lowercase())
|
(line is ReaderText.Text && line.line.text.lowercase() == chapter.title.lowercase())
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
|
|
||||||
readerText.add(
|
readerText.add(
|
||||||
0,
|
0,
|
||||||
ReaderText.Chapter(
|
chapter
|
||||||
title = chapterTitle
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +215,7 @@ class EpubTextParser @Inject constructor(
|
||||||
*/
|
*/
|
||||||
private suspend fun ZipFile.getChapterTitleMapFromToc(
|
private suspend fun ZipFile.getChapterTitleMapFromToc(
|
||||||
tocEntry: ZipEntry?
|
tocEntry: ZipEntry?
|
||||||
): Map<Title, List<String>>? {
|
): Map<Source, ReaderText.Chapter>? {
|
||||||
val tocContent = tocEntry?.let {
|
val tocContent = tocEntry?.let {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
getInputStream(it)
|
getInputStream(it)
|
||||||
|
|
@ -222,18 +224,43 @@ class EpubTextParser @Inject constructor(
|
||||||
val tocDocument = tocContent?.let { Jsoup.parse(it) }
|
val tocDocument = tocContent?.let { Jsoup.parse(it) }
|
||||||
|
|
||||||
if (tocDocument == null) return null
|
if (tocDocument == null) return null
|
||||||
var titleMap = mutableMapOf<Title, List<String>>()
|
var titleMap = mutableMapOf<Source, ReaderText.Chapter>()
|
||||||
|
|
||||||
tocDocument.select("navPoint").forEach { navPoint ->
|
tocDocument.select("navPoint").forEach { navPoint ->
|
||||||
val title = navPoint.selectFirst("navLabel > text")?.text()?.trim()
|
val title = navPoint.selectFirst("navLabel > text")?.text()
|
||||||
?: return@forEach
|
.let { title ->
|
||||||
|
if (title.isNullOrBlank()) return@forEach
|
||||||
|
title.trim()
|
||||||
|
}
|
||||||
|
|
||||||
val source = navPoint.selectFirst("content")?.attr("src")?.trim()
|
val source = navPoint.selectFirst("content")?.attr("src")?.trim()
|
||||||
.let {
|
.let { source ->
|
||||||
if (it == null) return@forEach
|
if (source.isNullOrBlank()) return@forEach
|
||||||
Uri.parse(it).path ?: it
|
Uri.parse(source).path ?: source
|
||||||
}.substringAfterLast(File.separator)
|
}.substringAfterLast(File.separator)
|
||||||
|
|
||||||
titleMap[source] = (titleMap[source] ?: emptyList()) + title
|
val parent = navPoint.parent()
|
||||||
|
.let { parent ->
|
||||||
|
if (parent == null) return@let null
|
||||||
|
if (!parent.tagName().equals("navPoint", ignoreCase = true)) return@let null
|
||||||
|
|
||||||
|
val parentSource = parent.selectFirst("content")?.attr("src")?.trim()
|
||||||
|
.let { parentSource ->
|
||||||
|
if (parentSource.isNullOrBlank()) return@forEach
|
||||||
|
Uri.parse(parentSource).path ?: parentSource
|
||||||
|
}.substringAfterLast(File.separator)
|
||||||
|
if (parentSource == source) return@let null
|
||||||
|
return@let parentSource
|
||||||
|
}
|
||||||
|
|
||||||
|
val chapter = ReaderText.Chapter(
|
||||||
|
title = titleMap[source]?.title.run {
|
||||||
|
if (this == null) return@run title
|
||||||
|
return@run "$this / $title"
|
||||||
|
},
|
||||||
|
nested = titleMap[source]?.nested ?: (parent != null)
|
||||||
|
)
|
||||||
|
titleMap[source] = chapter
|
||||||
}
|
}
|
||||||
|
|
||||||
return titleMap
|
return titleMap
|
||||||
|
|
@ -246,14 +273,12 @@ class EpubTextParser @Inject constructor(
|
||||||
*/
|
*/
|
||||||
private fun getChapterTitleFromToc(
|
private fun getChapterTitleFromToc(
|
||||||
chapterSource: String,
|
chapterSource: String,
|
||||||
chapterTitleMap: Map<String, List<String>>?
|
chapterTitleMap: Map<Source, ReaderText.Chapter>?
|
||||||
): String? {
|
): ReaderText.Chapter? {
|
||||||
if (chapterTitleMap.isNullOrEmpty()) return null
|
if (chapterTitleMap.isNullOrEmpty()) return null
|
||||||
return chapterTitleMap
|
return chapterTitleMap.getOrElse(chapterSource.substringAfterLast(File.separator)) {
|
||||||
.getOrElse(chapterSource.substringAfterLast(File.separator)) { null }
|
null
|
||||||
?.joinToString(separator = " / ")
|
}
|
||||||
?.ifBlank { null }
|
|
||||||
?.trim()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,8 @@ class Fb2TextParser @Inject constructor(
|
||||||
if (!chapterAdded && line.clearAllMarkdown().isNotBlank()) {
|
if (!chapterAdded && line.clearAllMarkdown().isNotBlank()) {
|
||||||
readerText.add(
|
readerText.add(
|
||||||
0, ReaderText.Chapter(
|
0, ReaderText.Chapter(
|
||||||
title = line.clearAllMarkdown()
|
title = line.clearAllMarkdown(),
|
||||||
|
nested = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
chapterAdded = true
|
chapterAdded = true
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,8 @@ class PdfTextParser @Inject constructor(
|
||||||
if (!chapterAdded && line.clearAllMarkdown().isNotBlank()) {
|
if (!chapterAdded && line.clearAllMarkdown().isNotBlank()) {
|
||||||
readerText.add(
|
readerText.add(
|
||||||
0, ReaderText.Chapter(
|
0, ReaderText.Chapter(
|
||||||
title = line.clearAllMarkdown()
|
title = line.clearAllMarkdown(),
|
||||||
|
nested = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
chapterAdded = true
|
chapterAdded = true
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,8 @@ class TxtTextParser @Inject constructor(
|
||||||
if (!chapterAdded && line.clearAllMarkdown().isNotBlank()) {
|
if (!chapterAdded && line.clearAllMarkdown().isNotBlank()) {
|
||||||
readerText.add(
|
readerText.add(
|
||||||
0, ReaderText.Chapter(
|
0, ReaderText.Chapter(
|
||||||
title = line.clearAllMarkdown()
|
title = line.clearAllMarkdown(),
|
||||||
|
nested = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
chapterAdded = true
|
chapterAdded = true
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* Book's Story — free and open-source Material You eBook reader.
|
||||||
|
* Copyright (C) 2024-2025 Acclorite
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.acclorite.book_story.domain.reader
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class ExpandableChapter(
|
||||||
|
val parent: ReaderText.Chapter,
|
||||||
|
val expanded: Boolean,
|
||||||
|
val chapters: List<ReaderText.Chapter>?
|
||||||
|
)
|
||||||
|
|
@ -9,11 +9,16 @@ package ua.acclorite.book_story.domain.reader
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
sealed class ReaderText {
|
sealed class ReaderText {
|
||||||
@Immutable
|
@Immutable
|
||||||
data class Chapter(val title: String) : ReaderText()
|
data class Chapter(
|
||||||
|
val id: UUID = UUID.randomUUID(),
|
||||||
|
val title: String,
|
||||||
|
val nested: Boolean
|
||||||
|
) : ReaderText()
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class Text(val line: AnnotatedString) : ReaderText()
|
data class Text(val line: AnnotatedString) : ReaderText()
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
@ -54,7 +55,8 @@ fun ModalDrawerSelectableItem(
|
||||||
.clickable(enabled = enabled) {
|
.clickable(enabled = enabled) {
|
||||||
onClick()
|
onClick()
|
||||||
}
|
}
|
||||||
.padding(horizontal = 18.dp, vertical = 18.dp)
|
.padding(horizontal = 18.dp, vertical = 18.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalContentColor provides if (selected) MaterialTheme.colorScheme.onSecondaryContainer
|
LocalContentColor provides if (selected) MaterialTheme.colorScheme.onSecondaryContainer
|
||||||
|
|
|
||||||
|
|
@ -6,24 +6,32 @@
|
||||||
|
|
||||||
package ua.acclorite.book_story.presentation.reader
|
package ua.acclorite.book_story.presentation.reader
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.ArrowDropUp
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
|
import ua.acclorite.book_story.domain.reader.ExpandableChapter
|
||||||
import ua.acclorite.book_story.domain.reader.ReaderText.Chapter
|
import ua.acclorite.book_story.domain.reader.ReaderText.Chapter
|
||||||
import ua.acclorite.book_story.presentation.core.components.common.StyledText
|
import ua.acclorite.book_story.presentation.core.components.common.StyledText
|
||||||
import ua.acclorite.book_story.presentation.core.components.modal_drawer.ModalDrawer
|
import ua.acclorite.book_story.presentation.core.components.modal_drawer.ModalDrawer
|
||||||
import ua.acclorite.book_story.presentation.core.components.modal_drawer.ModalDrawerSelectableItem
|
import ua.acclorite.book_story.presentation.core.components.modal_drawer.ModalDrawerSelectableItem
|
||||||
import ua.acclorite.book_story.presentation.core.components.modal_drawer.ModalDrawerTitleItem
|
import ua.acclorite.book_story.presentation.core.components.modal_drawer.ModalDrawerTitleItem
|
||||||
import ua.acclorite.book_story.presentation.core.util.calculateProgress
|
import ua.acclorite.book_story.presentation.core.util.calculateProgress
|
||||||
|
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
|
||||||
import ua.acclorite.book_story.ui.reader.ReaderEvent
|
import ua.acclorite.book_story.ui.reader.ReaderEvent
|
||||||
|
import ua.acclorite.book_story.ui.theme.ExpandingTransition
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ReaderChaptersDrawer(
|
fun ReaderChaptersDrawer(
|
||||||
|
|
@ -34,15 +42,43 @@ fun ReaderChaptersDrawer(
|
||||||
scrollToChapter: (ReaderEvent.OnScrollToChapter) -> Unit,
|
scrollToChapter: (ReaderEvent.OnScrollToChapter) -> Unit,
|
||||||
dismissDrawer: (ReaderEvent.OnDismissDrawer) -> Unit
|
dismissDrawer: (ReaderEvent.OnDismissDrawer) -> Unit
|
||||||
) {
|
) {
|
||||||
val currentChapterIndex = remember(chapters, currentChapter) {
|
val expandableChapters = remember(show, chapters, currentChapter) {
|
||||||
derivedStateOf {
|
mutableStateListOf<ExpandableChapter>().apply {
|
||||||
chapters.indexOf(currentChapter).takeIf { it != -1 } ?: 0
|
var index = 0
|
||||||
|
while (index < chapters.size) {
|
||||||
|
val chapter = chapters.getOrNull(index) ?: continue
|
||||||
|
when (chapter.nested) {
|
||||||
|
false -> {
|
||||||
|
val children = chapters.drop(index + 1).takeWhile { it.nested }
|
||||||
|
add(
|
||||||
|
ExpandableChapter(
|
||||||
|
parent = chapter,
|
||||||
|
expanded = chapter.id == currentChapter?.id ||
|
||||||
|
children.any { it.id == currentChapter?.id },
|
||||||
|
chapters = children.takeIf { it.isNotEmpty() }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
index += children.size + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
true -> {
|
||||||
|
add(
|
||||||
|
ExpandableChapter(
|
||||||
|
parent = chapter.copy(nested = false),
|
||||||
|
expanded = false,
|
||||||
|
chapters = null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModalDrawer(
|
ModalDrawer(
|
||||||
show = show,
|
show = show,
|
||||||
startIndex = currentChapterIndex.value,
|
startIndex = chapters.indexOf(currentChapter).takeIf { it != -1 } ?: 0,
|
||||||
onDismissRequest = { dismissDrawer(ReaderEvent.OnDismissDrawer) },
|
onDismissRequest = { dismissDrawer(ReaderEvent.OnDismissDrawer) },
|
||||||
header = {
|
header = {
|
||||||
ModalDrawerTitleItem(
|
ModalDrawerTitleItem(
|
||||||
|
|
@ -50,30 +86,88 @@ fun ReaderChaptersDrawer(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
itemsIndexed(chapters, key = { index, _ -> index }) { index, chapter ->
|
expandableChapters.forEach { expandableChapter ->
|
||||||
val selected = rememberSaveable(index, currentChapterIndex) {
|
item {
|
||||||
index == currentChapterIndex.value
|
ModalDrawerSelectableItem(
|
||||||
|
selected = expandableChapter.parent.id == currentChapter?.id,
|
||||||
|
onClick = {
|
||||||
|
scrollToChapter(
|
||||||
|
ReaderEvent.OnScrollToChapter(
|
||||||
|
chapter = expandableChapter.parent
|
||||||
|
)
|
||||||
|
)
|
||||||
|
dismissDrawer(ReaderEvent.OnDismissDrawer)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
StyledText(
|
||||||
|
text = expandableChapter.parent.title,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if (expandableChapter.parent == currentChapter) {
|
||||||
|
Spacer(modifier = Modifier.width(18.dp))
|
||||||
|
StyledText(text = "${currentChapterProgress.calculateProgress(0)}%")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expandableChapter.chapters.isNullOrEmpty()) {
|
||||||
|
Spacer(modifier = Modifier.width(18.dp))
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.ArrowDropUp,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
expandableChapters.indexOf(expandableChapter)
|
||||||
|
.also { chapterIndex ->
|
||||||
|
if (chapterIndex == -1) return@noRippleClickable
|
||||||
|
expandableChapters[chapterIndex] =
|
||||||
|
expandableChapter.copy(
|
||||||
|
expanded = !expandableChapter.expanded
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.rotate(
|
||||||
|
animateFloatAsState(
|
||||||
|
targetValue = if (expandableChapter.expanded) 0f else -180f
|
||||||
|
).value
|
||||||
|
),
|
||||||
|
contentDescription = stringResource(
|
||||||
|
id = if (expandableChapter.expanded) R.string.collapse_content_desc
|
||||||
|
else R.string.expand_content_desc
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModalDrawerSelectableItem(
|
if (!expandableChapter.chapters.isNullOrEmpty()) {
|
||||||
selected = selected,
|
items(expandableChapter.chapters) { chapter ->
|
||||||
onClick = {
|
ExpandingTransition(visible = expandableChapter.expanded) {
|
||||||
scrollToChapter(
|
ModalDrawerSelectableItem(
|
||||||
ReaderEvent.OnScrollToChapter(
|
selected = chapter.id == currentChapter?.id,
|
||||||
chapter = chapter
|
onClick = {
|
||||||
)
|
scrollToChapter(
|
||||||
)
|
ReaderEvent.OnScrollToChapter(
|
||||||
dismissDrawer(ReaderEvent.OnDismissDrawer)
|
chapter = chapter
|
||||||
}
|
)
|
||||||
) {
|
)
|
||||||
StyledText(
|
dismissDrawer(ReaderEvent.OnDismissDrawer)
|
||||||
text = chapter.title,
|
}
|
||||||
modifier = Modifier.weight(1f),
|
) {
|
||||||
maxLines = 1
|
Spacer(modifier = Modifier.width(18.dp))
|
||||||
)
|
|
||||||
if (selected) {
|
StyledText(
|
||||||
Spacer(modifier = Modifier.width(18.dp))
|
text = chapter.title,
|
||||||
StyledText(text = "${currentChapterProgress.calculateProgress(1)}%")
|
modifier = Modifier.weight(1f),
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if (chapter == currentChapter) {
|
||||||
|
Spacer(modifier = Modifier.width(18.dp))
|
||||||
|
StyledText(text = "${currentChapterProgress.calculateProgress(0)}%")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,12 @@ fun LazyItemScope.ReaderLayoutTextChapter(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = sidePadding)
|
.padding(horizontal = sidePadding)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(
|
style = (if (!chapter.nested) MaterialTheme.typography.headlineMedium
|
||||||
color = fontColor,
|
else MaterialTheme.typography.headlineSmall)
|
||||||
textAlign = chapterTitleAlignment.textAlignment
|
.copy(
|
||||||
),
|
color = fontColor,
|
||||||
|
textAlign = chapterTitleAlignment.textAlignment
|
||||||
|
),
|
||||||
highlightText = highlightedReading,
|
highlightText = highlightedReading,
|
||||||
highlightThickness = highlightedReadingThickness
|
highlightThickness = highlightedReadingThickness
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -480,5 +480,7 @@
|
||||||
<string name="show_less_content_desc">Показати менше</string>
|
<string name="show_less_content_desc">Показати менше</string>
|
||||||
<string name="show_more_content_desc">Показати більше</string>
|
<string name="show_more_content_desc">Показати більше</string>
|
||||||
<string name="edit_content_desc">Редагувати поле</string>
|
<string name="edit_content_desc">Редагувати поле</string>
|
||||||
|
<string name="expand_content_desc">Розгорнути</string>
|
||||||
|
<string name="collapse_content_desc">Згорнути</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -639,5 +639,7 @@
|
||||||
<string name="show_less_content_desc">Show less</string>
|
<string name="show_less_content_desc">Show less</string>
|
||||||
<string name="show_more_content_desc">Show more</string>
|
<string name="show_more_content_desc">Show more</string>
|
||||||
<string name="edit_content_desc">Edit field</string>
|
<string name="edit_content_desc">Edit field</string>
|
||||||
|
<string name="expand_content_desc">Expand</string>
|
||||||
|
<string name="collapse_content_desc">Collapse</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue