fix: 优化书籍详情内存占用

This commit is contained in:
HapeLee
2026-06-20 00:14:40 +08:00
parent c45e4355ff
commit 70c88cf3f4
4 changed files with 192 additions and 68 deletions
@@ -1,6 +1,7 @@
package io.legado.app.ui.book.info package io.legado.app.ui.book.info
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Stable
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.BookSource import io.legado.app.data.entities.BookSource
@@ -13,8 +14,8 @@ import kotlinx.collections.immutable.persistentListOf
const val READER_RESULT_DELETED = 100 const val READER_RESULT_DELETED = 100
data class BookInfoUiState( data class BookInfoUiState(
val book: Book? = null, val book: BookInfoBookUi? = null,
val chapterList: List<BookChapter> = emptyList(), val hasChapters: Boolean = false,
val webFiles: List<BookInfoWebFile> = emptyList(), val webFiles: List<BookInfoWebFile> = emptyList(),
val kindLabels: List<String> = emptyList(), val kindLabels: List<String> = emptyList(),
val groupNames: String? = null, val groupNames: String? = null,
@@ -22,7 +23,7 @@ data class BookInfoUiState(
val readRecordTotalTime: Long = 0L, val readRecordTotalTime: Long = 0L,
val readRecordTimelineDays: List<ReadRecordTimelineDay> = emptyList(), val readRecordTimelineDays: List<ReadRecordTimelineDay> = emptyList(),
val inBookshelf: Boolean = false, val inBookshelf: Boolean = false,
val bookSource: BookSource? = null, val bookSource: BookInfoSourceUi? = null,
val relatedBooks: ImmutableList<RelatedBooksUi> = persistentListOf(), val relatedBooks: ImmutableList<RelatedBooksUi> = persistentListOf(),
val isTocLoading: Boolean = true, val isTocLoading: Boolean = true,
val isBusy: Boolean = false, val isBusy: Boolean = false,
@@ -33,11 +34,39 @@ data class BookInfoUiState(
val dialog: BookInfoDialog? = null, val dialog: BookInfoDialog? = null,
) )
@Stable
data class BookInfoBookUi(
val bookUrl: String,
val name: String,
val author: String,
val realAuthor: String,
val origin: String,
val originName: String,
val coverPath: String?,
val group: Long,
val isLocal: Boolean,
val type: Int,
val canUpdate: Boolean,
val splitLongChapter: Boolean,
val durChapterTitle: String?,
val latestChapterTitle: String?,
val totalChapterNum: Int,
val durChapterIndex: Int,
val remark: String?,
val displayIntro: String?,
)
@Stable
data class BookInfoSourceUi(
val sourceUrl: String,
val hasLogin: Boolean,
)
sealed interface BookInfoSheet { sealed interface BookInfoSheet {
data object None : BookInfoSheet data object None : BookInfoSheet
data object CoverPicker : BookInfoSheet data object CoverPicker : BookInfoSheet
data object GroupPicker : BookInfoSheet data object GroupPicker : BookInfoSheet
data object SourcePicker : BookInfoSheet data class SourcePicker(val oldBook: Book) : BookInfoSheet
data object ReadRecord : BookInfoSheet data object ReadRecord : BookInfoSheet
data class WebFiles(val openAfterImport: Boolean) : BookInfoSheet data class WebFiles(val openAfterImport: Boolean) : BookInfoSheet
data class ArchiveEntries( data class ArchiveEntries(
@@ -64,12 +64,10 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil.ImageLoader import coil.ImageLoader
import coil.size.Size
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.help.book.isLocal
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.ui.config.coverConfig.CoverConfig import io.legado.app.ui.config.coverConfig.CoverConfig
import io.legado.app.ui.main.homepage.modules.BannerModule import io.legado.app.ui.main.homepage.modules.BannerModule
@@ -252,7 +250,7 @@ private fun BookInfoScreenContent(
} }
BookInfoSummary( BookInfoSummary(
book = book, book = book,
chapterList = state.chapterList, hasChapters = state.hasChapters,
onRemarkClick = { onIntent(BookInfoIntent.RemarkClick) }, onRemarkClick = { onIntent(BookInfoIntent.RemarkClick) },
) )
} }
@@ -294,10 +292,10 @@ private fun BookInfoScreenContent(
onConfirm = { onIntent(BookInfoIntent.SelectGroup(it)) }, onConfirm = { onIntent(BookInfoIntent.SelectGroup(it)) },
) )
} }
BookInfoSheet.SourcePicker -> state.book?.let { book -> is BookInfoSheet.SourcePicker -> {
ChangeSourceSheet( ChangeSourceSheet(
show = currentSheet == BookInfoSheet.SourcePicker, show = currentSheet is BookInfoSheet.SourcePicker,
oldBook = book, oldBook = sheet.oldBook,
onDismissRequest = { onIntent(BookInfoIntent.DismissSheet) }, onDismissRequest = { onIntent(BookInfoIntent.DismissSheet) },
onReplace = { source, newBook, toc, options -> onReplace = { source, newBook, toc, options ->
onIntent(BookInfoIntent.ReplaceWithSource(source, newBook, toc, options)) onIntent(BookInfoIntent.ReplaceWithSource(source, newBook, toc, options))
@@ -419,12 +417,12 @@ private fun BookInfoTransparentTopAppBar(
} }
@Composable @Composable
private fun rememberBookInfoColorTheme(book: Book?): ThemeOverrideState? { private fun rememberBookInfoColorTheme(book: BookInfoBookUi?): ThemeOverrideState? {
val useDefaultCover = AppConfig.useDefaultCover || book?.customCoverUrl == "use_default_cover" val useDefaultCover = AppConfig.useDefaultCover || book?.coverPath == "use_default_cover"
if (useDefaultCover) return null if (useDefaultCover) return null
val imageLoader = koinInject<ImageLoader>() val imageLoader = koinInject<ImageLoader>()
val coverPath = book?.getDisplayCover() val coverPath = book?.coverPath
val sourceOrigin = book?.origin val sourceOrigin = book?.origin
val loadOnlyWifi = CoverConfig.loadCoverOnlyWifi val loadOnlyWifi = CoverConfig.loadCoverOnlyWifi
val requestKey = remember(coverPath, sourceOrigin, loadOnlyWifi) { val requestKey = remember(coverPath, sourceOrigin, loadOnlyWifi) {
@@ -480,8 +478,21 @@ private fun BookInfoTopBarActions(
@Composable @Composable
private fun BookInfoBackdrop( private fun BookInfoBackdrop(
book: Book, book: BookInfoBookUi,
) { ) {
val backdropState = remember(
book.name,
book.author,
book.coverPath,
book.origin,
) {
BookInfoBackdropState(
name = book.name,
author = book.author,
coverPath = book.coverPath,
sourceOrigin = book.origin,
)
}
val seedOverlay = lerp( val seedOverlay = lerp(
LegadoTheme.colorScheme.secondaryContainer, LegadoTheme.colorScheme.secondaryContainer,
LegadoTheme.seedColor, LegadoTheme.seedColor,
@@ -489,21 +500,25 @@ private fun BookInfoBackdrop(
) )
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
Crossfade( Crossfade(
targetState = book, targetState = backdropState,
animationSpec = tween(800), animationSpec = tween(800),
label = "BackdropCrossfade" label = "BackdropCrossfade"
) { currentBook -> ) { currentBook ->
BookCoverImage( BookCoverImage(
name = currentBook.name, name = currentBook.name,
author = currentBook.author, author = currentBook.author,
path = currentBook.getDisplayCover(), path = currentBook.coverPath,
sourceOrigin = currentBook.origin, sourceOrigin = currentBook.sourceOrigin,
memoryCacheKey = currentBook.coverPath?.let { "$it#book-info-backdrop" },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(480.dp) .height(480.dp)
.blur(24.dp), .blur(24.dp),
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
showLoadingPlaceholder = false, showLoadingPlaceholder = false,
requestBuilder = {
size(Size(384, 384))
}
) )
} }
Box( Box(
@@ -530,6 +545,13 @@ private fun BookInfoBackdrop(
} }
} }
private data class BookInfoBackdropState(
val name: String,
val author: String,
val coverPath: String?,
val sourceOrigin: String?,
)
@Composable @Composable
private fun BookInfoOverflowMenu( private fun BookInfoOverflowMenu(
expanded: Boolean, expanded: Boolean,
@@ -563,7 +585,7 @@ private fun BookInfoOverflowMenu(
onClick = { onMenuAction(BookInfoMenuAction.Upload) } onClick = { onMenuAction(BookInfoMenuAction.Upload) }
) )
} }
if (!state.bookSource?.loginUrl.isNullOrBlank()) { if (state.bookSource?.hasLogin == true) {
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = stringResource(R.string.login), text = stringResource(R.string.login),
onClick = { onMenuAction(BookInfoMenuAction.Login) } onClick = { onMenuAction(BookInfoMenuAction.Login) }
@@ -602,7 +624,7 @@ private fun BookInfoOverflowMenu(
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = stringResource(R.string.split_long_chapter), text = stringResource(R.string.split_long_chapter),
onClick = { onMenuAction(BookInfoMenuAction.ToggleSplitLongChapter) }, onClick = { onMenuAction(BookInfoMenuAction.ToggleSplitLongChapter) },
isSelected = book.getSplitLongChapter() isSelected = book.splitLongChapter
) )
} }
RoundDropdownMenuItem( RoundDropdownMenuItem(
@@ -623,7 +645,7 @@ private fun BookInfoOverflowMenu(
@Composable @Composable
private fun BookInfoHeader( private fun BookInfoHeader(
book: Book, book: BookInfoBookUi,
kindLabels: List<String>, kindLabels: List<String>,
groupNames: String?, groupNames: String?,
onCoverClick: () -> Unit, onCoverClick: () -> Unit,
@@ -668,7 +690,7 @@ private fun BookInfoHeader(
CoilBookCover( CoilBookCover(
name = book.name, name = book.name,
author = book.author, author = book.author,
path = book.getDisplayCover(), path = book.coverPath,
sourceOrigin = book.origin, sourceOrigin = book.origin,
modifier = Modifier modifier = Modifier
.width(112.dp) .width(112.dp)
@@ -720,7 +742,7 @@ private fun BookInfoHeader(
} }
} }
AnimatedTextLine( AnimatedTextLine(
text = stringResource(R.string.author_show, book.getRealAuthor()), text = stringResource(R.string.author_show, book.realAuthor),
style = LegadoTheme.typography.bodyLarge, style = LegadoTheme.typography.bodyLarge,
color = LegadoTheme.colorScheme.onSurfaceVariant, color = LegadoTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.combinedClickable( modifier = Modifier.combinedClickable(
@@ -886,8 +908,8 @@ private fun BookInfoActionCard(
@Composable @Composable
private fun BookInfoSummary( private fun BookInfoSummary(
book: Book, book: BookInfoBookUi,
chapterList: List<BookChapter>, hasChapters: Boolean,
onRemarkClick: () -> Unit, onRemarkClick: () -> Unit,
) { ) {
Column( Column(
@@ -928,7 +950,7 @@ private fun BookInfoSummary(
color = LegadoTheme.colorScheme.secondary, color = LegadoTheme.colorScheme.secondary,
) )
} }
if (chapterList.isEmpty()) { if (!hasChapters) {
AnimatedTextLine( AnimatedTextLine(
text = stringResource(R.string.error_load_toc), text = stringResource(R.string.error_load_toc),
style = LegadoTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
@@ -952,7 +974,7 @@ private fun BookInfoSummary(
} }
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
AnimatedTextLine( AnimatedTextLine(
text = book.getDisplayIntro().orEmpty().ifBlank { stringResource(R.string.intro_show_null) }, text = book.displayIntro.orEmpty().ifBlank { stringResource(R.string.intro_show_null) },
style = LegadoTheme.typography.bodyMedium, style = LegadoTheme.typography.bodyMedium,
) )
} }
@@ -62,12 +62,14 @@ import io.legado.app.utils.postEvent
import io.legado.app.utils.splitNotBlank import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.toastOnUi import io.legado.app.utils.toastOnUi
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
@@ -76,6 +78,7 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
class BookInfoViewModel( class BookInfoViewModel(
@@ -119,6 +122,7 @@ class BookInfoViewModel(
private var changeSourceCoroutine: Coroutine<*>? = null private var changeSourceCoroutine: Coroutine<*>? = null
private var readRecordObserveJob: Job? = null private var readRecordObserveJob: Job? = null
private var relatedBooksLoadJob: Job? = null
fun initData(intent: Intent) { fun initData(intent: Intent) {
initData( initData(
@@ -162,6 +166,7 @@ class BookInfoViewModel(
bookSource = null bookSource = null
chapterChanged = false chapterChanged = false
clearReadRecordObserve() clearReadRecordObserve()
relatedBooksLoadJob?.cancel()
syncUiState() syncUiState()
execute { execute {
val dbBook = appDb.bookDao.getBook(bookUrl) val dbBook = appDb.bookDao.getBook(bookUrl)
@@ -214,7 +219,8 @@ class BookInfoViewModel(
?.let { showDialog(BookInfoDialog.PhotoPreview(it)) } ?.let { showDialog(BookInfoDialog.PhotoPreview(it)) }
BookInfoIntent.GroupClick -> setSheet(BookInfoSheet.GroupPicker) BookInfoIntent.GroupClick -> setSheet(BookInfoSheet.GroupPicker)
BookInfoIntent.ChangeSourceClick -> setSheet(BookInfoSheet.SourcePicker) BookInfoIntent.ChangeSourceClick -> currentBook?.uiCopy()
?.let { setSheet(BookInfoSheet.SourcePicker(it)) }
BookInfoIntent.ReadRecordClick -> setSheet(BookInfoSheet.ReadRecord) BookInfoIntent.ReadRecordClick -> setSheet(BookInfoSheet.ReadRecord)
BookInfoIntent.RemarkClick -> showDialog(BookInfoDialog.EditRemark(currentBook?.remark)) BookInfoIntent.RemarkClick -> showDialog(BookInfoDialog.EditRemark(currentBook?.remark))
is BookInfoIntent.SaveCover -> { is BookInfoIntent.SaveCover -> {
@@ -733,7 +739,7 @@ class BookInfoViewModel(
} else { } else {
loadChapter(loadedBook, runPreUpdateJs) loadChapter(loadedBook, runPreUpdateJs)
} }
loadRelatedBooks(loadedBook, source) scheduleRelatedBooksLoad(loadedBook, source)
}.onError { }.onError {
AppLog.put("获取书籍信息失败\n${it.localizedMessage}", it) AppLog.put("获取书籍信息失败\n${it.localizedMessage}", it)
context.toastOnUi(R.string.error_get_book_info) context.toastOnUi(R.string.error_get_book_info)
@@ -792,7 +798,7 @@ class BookInfoViewModel(
if (chapters.isNotEmpty()) { if (chapters.isNotEmpty()) {
currentChapterList = chapters currentChapterList = chapters
syncUiState(isTocLoading = false) syncUiState(isTocLoading = false)
source?.let { loadRelatedBooks(book, it) } source?.let { scheduleRelatedBooksLoad(book, it) }
} else { } else {
loadChapter(book) loadChapter(book)
} }
@@ -1291,8 +1297,8 @@ class BookInfoViewModel(
private fun syncUiState(isTocLoading: Boolean = _uiState.value.isTocLoading) { private fun syncUiState(isTocLoading: Boolean = _uiState.value.isTocLoading) {
_uiState.update { _uiState.update {
it.copy( it.copy(
book = currentBook?.uiCopy(), book = currentBook?.toBookInfoBookUi(),
chapterList = currentChapterList, hasChapters = currentChapterList.isNotEmpty(),
webFiles = currentWebFiles, webFiles = currentWebFiles,
relatedBooks = currentRelatedBooks.toImmutableList(), relatedBooks = currentRelatedBooks.toImmutableList(),
kindLabels = currentKindLabels, kindLabels = currentKindLabels,
@@ -1301,7 +1307,7 @@ class BookInfoViewModel(
readRecordTotalTime = currentReadRecordTotalTime, readRecordTotalTime = currentReadRecordTotalTime,
readRecordTimelineDays = currentReadRecordTimelineDays, readRecordTimelineDays = currentReadRecordTimelineDays,
inBookshelf = inBookshelf, inBookshelf = inBookshelf,
bookSource = bookSource, bookSource = bookSource?.toBookInfoSourceUi(),
isTocLoading = isTocLoading, isTocLoading = isTocLoading,
deleteAlertEnabled = LocalConfig.bookInfoDeleteAlert, deleteAlertEnabled = LocalConfig.bookInfoDeleteAlert,
deleteOriginal = LocalConfig.deleteBookOriginal, deleteOriginal = LocalConfig.deleteBookOriginal,
@@ -1332,14 +1338,50 @@ class BookInfoViewModel(
) )
} }
private fun loadRelatedBooks(book: Book, source: BookSource) { private fun scheduleRelatedBooksLoad(
val modulesJson = source.ruleBookInfo?.relatedBooks book: Book,
if (modulesJson.isNullOrBlank()) { source: BookSource,
delayMillis: Long = 350L,
) {
relatedBooksLoadJob?.cancel()
relatedBooksLoadJob = viewModelScope.launch {
delay(delayMillis)
if (!isCurrentBookSource(book, source)) return@launch
val modules = parseRelatedBookModules(source)
if (modules.isEmpty()) {
currentRelatedBooks = emptyList() currentRelatedBooks = emptyList()
syncUiState() syncUiState()
return return@launch
} }
val modules = try {
try {
val result = withContext(IO) {
loadRelatedBooks(book, source, modules)
}
if (!isCurrentBookSource(book, source)) return@launch
currentRelatedBooks = result
syncUiState()
} catch (e: CancellationException) {
throw e
} catch (e: Throwable) {
if (!isCurrentBookSource(book, source)) return@launch
currentRelatedBooks = emptyList()
syncUiState()
}
}
}
private fun isCurrentBookSource(book: Book, source: BookSource): Boolean {
return currentBook?.bookUrl == book.bookUrl && bookSource?.bookSourceUrl == source.bookSourceUrl
}
private fun parseRelatedBookModules(source: BookSource): List<RelatedBooksDef> {
val modulesJson = source.ruleBookInfo?.relatedBooks
if (modulesJson.isNullOrBlank()) {
return emptyList()
}
return try {
GSON.fromJsonArray<RelatedBooksDef>(modulesJson) GSON.fromJsonArray<RelatedBooksDef>(modulesJson)
.getOrNull() .getOrNull()
?.filter { !it.url.isNullOrBlank() } ?.filter { !it.url.isNullOrBlank() }
@@ -1348,37 +1390,34 @@ class BookInfoViewModel(
} catch (e: Exception) { } catch (e: Exception) {
emptyList() emptyList()
} }
if (modules.isEmpty()) {
currentRelatedBooks = emptyList()
syncUiState()
return
} }
execute {
coroutineScope { private suspend fun loadRelatedBooks(
book: Book,
source: BookSource,
modules: List<RelatedBooksDef>,
): List<RelatedBooksUi> {
return coroutineScope {
modules.map { def -> modules.map { def ->
async { async {
val url = def.url.orEmpty()
val (resolvedUrl, books) = try { val (resolvedUrl, books) = try {
resolveAndExplore(source, def.url!!, book) resolveAndExplore(source, url, book)
} catch (e: CancellationException) {
throw e
} catch (e: Exception) { } catch (e: Exception) {
def.url!! to emptyList() url to emptyList()
} }
RelatedBooksUi( RelatedBooksUi(
key = def.key ?: def.title.orEmpty(), key = def.key ?: def.title.orEmpty(),
title = def.title.orEmpty(), title = def.title.orEmpty(),
url = def.url!!, url = url,
resolvedUrl = resolvedUrl, resolvedUrl = resolvedUrl,
books = books.filter { it.bookUrl != book.bookUrl }.toImmutableList(), books = books.filter { it.bookUrl != book.bookUrl }.toImmutableList(),
) )
} }
}.awaitAll().filter { it.books.isNotEmpty() } }.awaitAll().filter { it.books.isNotEmpty() }
} }
}.onSuccess { result ->
currentRelatedBooks = result
syncUiState()
}.onError {
currentRelatedBooks = emptyList()
syncUiState()
}
} }
private suspend fun resolveAndExplore( private suspend fun resolveAndExplore(
@@ -1393,6 +1432,36 @@ class BookInfoViewModel(
_effects.tryEmit(effect) _effects.tryEmit(effect)
} }
private fun Book.toBookInfoBookUi(): BookInfoBookUi {
return BookInfoBookUi(
bookUrl = bookUrl,
name = name,
author = author,
realAuthor = getRealAuthor(),
origin = origin,
originName = originName,
coverPath = getDisplayCover(),
group = group,
isLocal = isLocal,
type = type,
canUpdate = canUpdate,
splitLongChapter = getSplitLongChapter(),
durChapterTitle = durChapterTitle,
latestChapterTitle = latestChapterTitle,
totalChapterNum = totalChapterNum,
durChapterIndex = durChapterIndex,
remark = remark,
displayIntro = getDisplayIntro(),
)
}
private fun BookSource.toBookInfoSourceUi(): BookInfoSourceUi {
return BookInfoSourceUi(
sourceUrl = bookSourceUrl,
hasLogin = !loginUrl.isNullOrBlank(),
)
}
private fun Book.uiCopy(): Book { private fun Book.uiCopy(): Book {
return copy().also { snapshot -> return copy().also { snapshot ->
snapshot.infoHtml = infoHtml snapshot.infoHtml = infoHtml
@@ -43,6 +43,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.graphics.withSave import androidx.core.graphics.withSave
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest
import io.legado.app.ui.config.coverConfig.CoverConfig import io.legado.app.ui.config.coverConfig.CoverConfig
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import org.koin.compose.koinInject import org.koin.compose.koinInject
@@ -58,6 +59,7 @@ fun BookCoverImage(
path: String?, path: String?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
sourceOrigin: String? = null, sourceOrigin: String? = null,
memoryCacheKey: String? = null,
ignoreUseDefaultCover: Boolean = false, ignoreUseDefaultCover: Boolean = false,
showLoadingPlaceholder: Boolean = true, showLoadingPlaceholder: Boolean = true,
contentScale: ContentScale = ContentScale.Crop, contentScale: ContentScale = ContentScale.Crop,
@@ -65,6 +67,7 @@ fun BookCoverImage(
onSuccess: (() -> Unit)? = null, onSuccess: (() -> Unit)? = null,
onError: (() -> Unit)? = null, onError: (() -> Unit)? = null,
sharedCoverKey: String? = null, sharedCoverKey: String? = null,
requestBuilder: ImageRequest.Builder.() -> Unit = {},
) { ) {
val context = LocalContext.current val context = LocalContext.current
val isNight = LegadoTheme.isDark val isNight = LegadoTheme.isDark
@@ -116,7 +119,8 @@ fun BookCoverImage(
sourceOrigin = sourceOrigin, sourceOrigin = sourceOrigin,
loadOnlyWifi = CoverConfig.loadCoverOnlyWifi, loadOnlyWifi = CoverConfig.loadCoverOnlyWifi,
crossfade = showLoadingPlaceholder, crossfade = showLoadingPlaceholder,
memoryCacheKey = finalPath, memoryCacheKey = memoryCacheKey ?: finalPath,
configure = requestBuilder,
), ),
contentDescription = null, contentDescription = null,
imageLoader = koinInject(), imageLoader = koinInject(),