[优化] 优化Tab栏样式
This commit is contained in:
@@ -421,7 +421,8 @@ fun TocScreen(
|
|||||||
selectedTabIndex = pagerState.currentPage,
|
selectedTabIndex = pagerState.currentPage,
|
||||||
edgePadding = 0.dp,
|
edgePadding = 0.dp,
|
||||||
divider = {},
|
divider = {},
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f),
|
||||||
|
containerColor = Color.Transparent
|
||||||
) {
|
) {
|
||||||
Tab(
|
Tab(
|
||||||
selected = pagerState.currentPage == 0,
|
selected = pagerState.currentPage == 0,
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ import io.legado.app.data.appDb
|
|||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
import io.legado.app.data.entities.BookChapter
|
import io.legado.app.data.entities.BookChapter
|
||||||
import io.legado.app.data.entities.Bookmark
|
import io.legado.app.data.entities.Bookmark
|
||||||
|
import io.legado.app.data.entities.ReplaceRule
|
||||||
import io.legado.app.help.book.BookHelp
|
import io.legado.app.help.book.BookHelp
|
||||||
import io.legado.app.help.book.ContentProcessor
|
import io.legado.app.help.book.ContentProcessor
|
||||||
import io.legado.app.help.book.isLocal
|
import io.legado.app.help.book.isLocal
|
||||||
import io.legado.app.help.bookmark.BookmarkExporter
|
import io.legado.app.help.bookmark.BookmarkExporter
|
||||||
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.model.CacheBook
|
import io.legado.app.model.CacheBook
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.model.localBook.LocalBook
|
import io.legado.app.model.localBook.LocalBook
|
||||||
@@ -26,6 +28,7 @@ import io.legado.app.ui.widget.components.list.SelectableItem
|
|||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
@@ -97,6 +100,14 @@ private data class TocUiConfig(
|
|||||||
val isReverse: Boolean
|
val isReverse: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private data class TitleCacheKey(
|
||||||
|
val bookUrl: String,
|
||||||
|
val useReplace: Boolean,
|
||||||
|
val rulesFingerprint: Int,
|
||||||
|
val chineseConverterType: Int,
|
||||||
|
val chapterCount: Int
|
||||||
|
)
|
||||||
|
|
||||||
data class FabAction(val icon: ImageVector, val label: String, val action: () -> Unit)
|
data class FabAction(val icon: ImageVector, val label: String, val action: () -> Unit)
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@@ -209,12 +220,17 @@ class TocViewModel(
|
|||||||
TocUiConfig(collapsed, useReplace, showWordCount, isReverse)
|
TocUiConfig(collapsed, useReplace, showWordCount, isReverse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val titleReplaceCache = MutableStateFlow<Map<Int, String>>(emptyMap())
|
||||||
|
private var titleCacheJob: Job? = null
|
||||||
|
private var lastTitleCacheKey: TitleCacheKey? = null
|
||||||
|
|
||||||
override val rawDataFlow: Flow<List<TocDomainItem>> = combine(
|
override val rawDataFlow: Flow<List<TocDomainItem>> = combine(
|
||||||
bookState.filterNotNull().map { it.bookUrl }.distinctUntilChanged()
|
bookState.filterNotNull().map { it.bookUrl }.distinctUntilChanged()
|
||||||
.flatMapLatest { appDb.bookChapterDao.getChapterListFlow(it) },
|
.flatMapLatest { appDb.bookChapterDao.getChapterListFlow(it) },
|
||||||
downloadContextFlow,
|
downloadContextFlow,
|
||||||
uiConfigFlow
|
uiConfigFlow,
|
||||||
) { originalChapters, downloadCtx, config ->
|
titleReplaceCache
|
||||||
|
) { originalChapters, downloadCtx, config, cachedTitles ->
|
||||||
val book = bookState.value ?: return@combine emptyList()
|
val book = bookState.value ?: return@combine emptyList()
|
||||||
|
|
||||||
val processedChapters = if (config.isReverse) {
|
val processedChapters = if (config.isReverse) {
|
||||||
@@ -227,11 +243,19 @@ class TocViewModel(
|
|||||||
ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
|
ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
|
||||||
} else emptyList()
|
} else emptyList()
|
||||||
|
|
||||||
|
updateTitleReplaceCacheIfNeeded(
|
||||||
|
book = book,
|
||||||
|
chapters = processedChapters,
|
||||||
|
replaceRules = replaceRules,
|
||||||
|
useReplace = config.useReplace
|
||||||
|
)
|
||||||
|
|
||||||
if (book.isLocal) {
|
if (book.isLocal) {
|
||||||
return@combine processedChapters.map { chapter ->
|
return@combine processedChapters.map { chapter ->
|
||||||
|
val baseTitle = chapter.getDisplayTitle(useReplace = false)
|
||||||
TocDomainItem(
|
TocDomainItem(
|
||||||
chapter = chapter,
|
chapter = chapter,
|
||||||
displayTitle = chapter.getDisplayTitle(replaceRules, true),
|
displayTitle = cachedTitles[chapter.index] ?: baseTitle,
|
||||||
downloadState = DownloadState.LOCAL
|
downloadState = DownloadState.LOCAL
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -251,9 +275,10 @@ class TocViewModel(
|
|||||||
else -> DownloadState.NONE
|
else -> DownloadState.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val baseTitle = chapter.getDisplayTitle(useReplace = false)
|
||||||
TocDomainItem(
|
TocDomainItem(
|
||||||
chapter,
|
chapter,
|
||||||
chapter.getDisplayTitle(replaceRules, true),
|
cachedTitles[chapter.index] ?: baseTitle,
|
||||||
downloadState
|
downloadState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -506,4 +531,56 @@ class TocViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateTitleReplaceCacheIfNeeded(
|
||||||
|
book: Book,
|
||||||
|
chapters: List<BookChapter>,
|
||||||
|
replaceRules: List<ReplaceRule>,
|
||||||
|
useReplace: Boolean
|
||||||
|
) {
|
||||||
|
val shouldUseReplace = useReplace && book.getUseReplaceRule() && replaceRules.isNotEmpty()
|
||||||
|
if (!shouldUseReplace) {
|
||||||
|
titleCacheJob?.cancel()
|
||||||
|
titleCacheJob = null
|
||||||
|
lastTitleCacheKey = null
|
||||||
|
if (titleReplaceCache.value.isNotEmpty()) {
|
||||||
|
titleReplaceCache.value = emptyMap()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val rulesFingerprint = replaceRules.fold(1) { acc, rule ->
|
||||||
|
var hash = acc
|
||||||
|
hash = 31 * hash + rule.id.hashCode()
|
||||||
|
hash = 31 * hash + rule.pattern.hashCode()
|
||||||
|
hash = 31 * hash + rule.replacement.hashCode()
|
||||||
|
hash = 31 * hash + rule.isRegex.hashCode()
|
||||||
|
hash = 31 * hash + rule.timeoutMillisecond.hashCode()
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = TitleCacheKey(
|
||||||
|
bookUrl = book.bookUrl,
|
||||||
|
useReplace = true,
|
||||||
|
rulesFingerprint = rulesFingerprint,
|
||||||
|
chineseConverterType = AppConfig.chineseConverterType,
|
||||||
|
chapterCount = chapters.size
|
||||||
|
)
|
||||||
|
|
||||||
|
val isJobActive = titleCacheJob?.isActive == true
|
||||||
|
if (key == lastTitleCacheKey && (isJobActive || titleReplaceCache.value.isNotEmpty())) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTitleCacheKey = key
|
||||||
|
titleCacheJob?.cancel()
|
||||||
|
titleReplaceCache.value = emptyMap()
|
||||||
|
titleCacheJob = viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
val newCache = HashMap<Int, String>(chapters.size)
|
||||||
|
chapters.forEach { chapter ->
|
||||||
|
newCache[chapter.index] = chapter.getDisplayTitle(replaceRules, true)
|
||||||
|
}
|
||||||
|
titleReplaceCache.value = newCache
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -50,6 +50,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.platform.ClipEntry
|
import androidx.compose.ui.platform.ClipEntry
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
@@ -65,7 +66,6 @@ import io.legado.app.base.BaseRuleEvent
|
|||||||
import io.legado.app.data.entities.ReplaceRule
|
import io.legado.app.data.entities.ReplaceRule
|
||||||
import io.legado.app.ui.widget.components.ActionItem
|
import io.legado.app.ui.widget.components.ActionItem
|
||||||
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
|
||||||
import io.legado.app.ui.widget.components.GroupManageBottomSheet
|
import io.legado.app.ui.widget.components.GroupManageBottomSheet
|
||||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||||
@@ -352,7 +352,7 @@ fun ReplaceRuleScreen(
|
|||||||
.coerceAtLeast(0),
|
.coerceAtLeast(0),
|
||||||
edgePadding = 0.dp,
|
edgePadding = 0.dp,
|
||||||
divider = {},
|
divider = {},
|
||||||
containerColor = GlassTopAppBarDefaults.containerColor(),
|
containerColor = Color.Transparent,
|
||||||
) {
|
) {
|
||||||
tabItems.forEachIndexed { index, title ->
|
tabItems.forEachIndexed { index, title ->
|
||||||
Tab(
|
Tab(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
VERSION_MAJOR=3
|
VERSION_MAJOR=3
|
||||||
VERSION_MINOR=26
|
VERSION_MINOR=26
|
||||||
VERSION_PATCH=8
|
VERSION_PATCH=9
|
||||||
VERSION_SUFFIX=0
|
VERSION_SUFFIX=0
|
||||||
# 1 = Pre, 0 = Release
|
# 1 = Pre, 0 = Release
|
||||||
Reference in New Issue
Block a user