[优化] 优化书架界面,增加开关以控制搜索按钮优先打开搜索页还是筛选栏

This commit is contained in:
HapeLee
2026-04-26 15:13:50 +08:00
parent 2989fe0405
commit 7e27686fbf
16 changed files with 644 additions and 425 deletions
@@ -241,6 +241,7 @@ object PreferKey {
const val bookshelfTitleCenter = "bookshelfTitleCenter" const val bookshelfTitleCenter = "bookshelfTitleCenter"
const val bookshelfTitleMaxLines = "bookshelfTitleMaxLines" const val bookshelfTitleMaxLines = "bookshelfTitleMaxLines"
const val bookshelfCoverShadow = "bookshelfCoverShadow" const val bookshelfCoverShadow = "bookshelfCoverShadow"
const val bookshelfSearchActionDirectToSearch = "bookshelfSearchActionDirectToSearch"
const val exploreLayoutGridLandscape = "exploreLayoutGridLandscape" const val exploreLayoutGridLandscape = "exploreLayoutGridLandscape"
@@ -58,7 +58,7 @@ import io.legado.app.ui.book.bookmark.AllBookmarkViewModel
import io.legado.app.ui.book.changecover.ChangeCoverViewModel import io.legado.app.ui.book.changecover.ChangeCoverViewModel
import io.legado.app.ui.book.changesource.ChangeBookSourceComposeViewModel import io.legado.app.ui.book.changesource.ChangeBookSourceComposeViewModel
import io.legado.app.ui.book.changesource.ChangeBookSourceViewModel import io.legado.app.ui.book.changesource.ChangeBookSourceViewModel
import io.legado.app.ui.book.cache.CacheViewModel import io.legado.app.ui.book.manage.BookshelfManageScreenViewModel
import io.legado.app.ui.book.explore.ExploreShowViewModel import io.legado.app.ui.book.explore.ExploreShowViewModel
import io.legado.app.ui.book.group.GroupViewModel import io.legado.app.ui.book.group.GroupViewModel
import io.legado.app.ui.book.import.local.ImportBookViewModel import io.legado.app.ui.book.import.local.ImportBookViewModel
@@ -72,7 +72,7 @@ import io.legado.app.ui.book.searchContent.SearchContentViewModel
import io.legado.app.ui.book.toc.TocViewModel import io.legado.app.ui.book.toc.TocViewModel
import io.legado.app.ui.book.toc.rule.TxtTocRuleViewModel import io.legado.app.ui.book.toc.rule.TxtTocRuleViewModel
import io.legado.app.ui.config.backupConfig.BackupConfigViewModel import io.legado.app.ui.config.backupConfig.BackupConfigViewModel
import io.legado.app.ui.config.cacheConfig.CacheConfig import io.legado.app.ui.config.bookshelfConfig.BookshelfManageScreenConfig
import io.legado.app.ui.config.coverConfig.CoverConfigViewModel import io.legado.app.ui.config.coverConfig.CoverConfigViewModel
import io.legado.app.ui.config.otherConfig.OtherConfigViewModel import io.legado.app.ui.config.otherConfig.OtherConfigViewModel
import io.legado.app.ui.config.readConfig.ReadConfigViewModel import io.legado.app.ui.config.readConfig.ReadConfigViewModel
@@ -124,7 +124,7 @@ val appModule = module {
singleOf(::ResolveBookShelfStateUseCase) singleOf(::ResolveBookShelfStateUseCase)
singleOf(::ShrinkDatabaseUseCase) singleOf(::ShrinkDatabaseUseCase)
singleOf(::WebDavBackupUseCase) singleOf(::WebDavBackupUseCase)
singleOf(::CacheConfig) singleOf(::BookshelfManageScreenConfig)
single<UploadRepository> { DirectLinkUploadRepository() } single<UploadRepository> { DirectLinkUploadRepository() }
single<AppStartupGateway> { AppStartupRepository(get()) } single<AppStartupGateway> { AppStartupRepository(get()) }
@@ -188,12 +188,12 @@ val appModule = module {
viewModelOf(::RssViewModel) viewModelOf(::RssViewModel)
viewModelOf(::SearchViewModel) viewModelOf(::SearchViewModel)
viewModel { viewModel {
CacheViewModel( BookshelfManageScreenViewModel(
application = get(), application = get(),
bookDao = get(), bookDao = get(),
bookGroupDao = get(), bookGroupDao = get(),
bookChapterDao = get(), bookChapterDao = get(),
cacheConfig = get(), bookshelfManageScreenConfig = get(),
batchCacheDownloadUseCase = get(), batchCacheDownloadUseCase = get(),
cacheBookChaptersUseCase = get(), cacheBookChaptersUseCase = get(),
changeBookSourceUseCase = get(), changeBookSourceUseCase = get(),
@@ -260,6 +260,7 @@ fun ChangeSourceSheet(
var loadingAction by remember { mutableStateOf(false) } var loadingAction by remember { mutableStateOf(false) }
var showOptionsMenu by rememberSaveable { mutableStateOf(false) } var showOptionsMenu by rememberSaveable { mutableStateOf(false) }
var showFilterMenu by rememberSaveable { mutableStateOf(false) } var showFilterMenu by rememberSaveable { mutableStateOf(false) }
val bookAddedToShelfText = stringResource(R.string.book_added_to_shelf)
val editSourceResult = rememberLauncherForActivityResult(StartActivityContract(BookSourceEditActivity::class.java)) { val editSourceResult = rememberLauncherForActivityResult(StartActivityContract(BookSourceEditActivity::class.java)) {
val origin = it.data?.getStringExtra("origin") ?: return@rememberLauncherForActivityResult val origin = it.data?.getStringExtra("origin") ?: return@rememberLauncherForActivityResult
@@ -490,7 +491,7 @@ fun ChangeSourceSheet(
pendingMigration = PendingSourceMigration(source, book, toc) pendingMigration = PendingSourceMigration(source, book, toc)
} else { } else {
onAddAsNew(book, toc) onAddAsNew(book, toc)
context.toastOnUi(context.getString(R.string.book_added_to_shelf)) context.toastOnUi(bookAddedToShelfText)
} }
actionBook = null actionBook = null
}, { }, {
@@ -1,4 +1,4 @@
package io.legado.app.ui.book.cache package io.legado.app.ui.book.manage
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
@@ -120,15 +120,16 @@ import io.legado.app.utils.verificationField
import io.legado.app.ui.theme.adaptiveHorizontalPadding import io.legado.app.ui.theme.adaptiveHorizontalPadding
import io.legado.app.ui.widget.components.button.MediumIconButton import io.legado.app.ui.widget.components.button.MediumIconButton
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
import sh.calvin.reorderable.ReorderableItem
import sh.calvin.reorderable.rememberReorderableLazyListState import sh.calvin.reorderable.rememberReorderableLazyListState
data class CacheFabAction( data class BookshelfManageFabAction(
val icon: androidx.compose.ui.graphics.vector.ImageVector, val icon: androidx.compose.ui.graphics.vector.ImageVector,
val label: String, val label: String,
val action: () -> Unit val action: () -> Unit
) )
private data class CacheListState( private data class BookshelfManageListState(
override val items: List<Book> = emptyList(), override val items: List<Book> = emptyList(),
override val selectedIds: Set<Any> = emptySet(), override val selectedIds: Set<Any> = emptySet(),
override val searchKey: String = "", override val searchKey: String = "",
@@ -137,21 +138,21 @@ private data class CacheListState(
) : ListUiState<Book> ) : ListUiState<Book>
@Composable @Composable
fun CacheRouteScreen( fun BookshelfManageRouteScreen(
groupId: Long, groupId: Long,
onBackClick: () -> Unit, onBackClick: () -> Unit,
viewModel: CacheViewModel = koinViewModel() viewModel: BookshelfManageScreenViewModel = koinViewModel()
) { ) {
LaunchedEffect(groupId) { LaunchedEffect(groupId) {
viewModel.dispatch(CacheIntent.Initialize(groupId)) viewModel.dispatch(BookshelfManageScreenIntent.Initialize(groupId))
} }
CacheScreen(viewModel = viewModel, onBackClick = onBackClick) BookshelfManageScreen(viewModel = viewModel, onBackClick = onBackClick)
} }
@OptIn(ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
private fun CacheScreen( private fun BookshelfManageScreen(
viewModel: CacheViewModel, viewModel: BookshelfManageScreenViewModel,
onBackClick: () -> Unit onBackClick: () -> Unit
) { ) {
val state by viewModel.uiState.collectAsStateWithLifecycle() val state by viewModel.uiState.collectAsStateWithLifecycle()
@@ -203,6 +204,7 @@ private fun CacheScreen(
val exportFileNameText = stringResource(R.string.export_file_name) val exportFileNameText = stringResource(R.string.export_file_name)
val resultAnalyzedText = stringResource(R.string.result_analyzed) val resultAnalyzedText = stringResource(R.string.result_analyzed)
val errorScopeInputText = stringResource(R.string.error_scope_input) val errorScopeInputText = stringResource(R.string.error_scope_input)
val noGroupText = stringResource(R.string.no_group)
val exportFileNameHintText = "书名:《{name}》 作者:{author}" val exportFileNameHintText = "书名:《{name}》 作者:{author}"
val exportFileNameHelpText = """ val exportFileNameHelpText = """
支持变量{name}书名{author}作者{group}分组{source}书源{remark}备注 支持变量{name}书名{author}作者{group}分组{source}书源{remark}备注
@@ -218,15 +220,15 @@ private fun CacheScreen(
val booksByUrl = remember(state.books) { state.books.associateBy { it.bookUrl } } val booksByUrl = remember(state.books) { state.books.associateBy { it.bookUrl } }
val userGroups = remember(state.groupList) { state.groupList.filter { it.groupId > 0L } } val userGroups = remember(state.groupList) { state.groupList.filter { it.groupId > 0L } }
val groupNameResolver: (Book) -> String = remember(userGroups, context) { val groupNameResolver: (Book) -> String = remember(userGroups, noGroupText) {
{ book -> { book ->
if (book.group <= 0L) { if (book.group <= 0L) {
context.getString(R.string.no_group) noGroupText
} else { } else {
val groups = userGroups.filter { val groups = userGroups.filter {
(book.group and it.groupId) > 0L (book.group and it.groupId) > 0L
} }
if (groups.isEmpty()) context.getString(R.string.no_group) if (groups.isEmpty()) noGroupText
else groups.joinToString("") { it.groupName } else groups.joinToString("") { it.groupName }
} }
} }
@@ -245,7 +247,7 @@ private fun CacheScreen(
} }
} }
val listUiState = remember(filteredBooks, selectedBookUrls, searchKey, isSearchMode) { val listUiState = remember(filteredBooks, selectedBookUrls, searchKey, isSearchMode) {
CacheListState( BookshelfManageListState(
items = filteredBooks, items = filteredBooks,
selectedIds = selectedBookUrls.mapTo(linkedSetOf()) { it as Any }, selectedIds = selectedBookUrls.mapTo(linkedSetOf()) { it as Any },
searchKey = searchKey, searchKey = searchKey,
@@ -327,9 +329,9 @@ private fun CacheScreen(
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.effects.collect { effect -> viewModel.effects.collect { effect ->
when (effect) { when (effect) {
is CacheEffect.ShowMessage -> context.toastOnUi(effect.message) is BookshelfManageScreenEffect.ShowMessage -> context.toastOnUi(effect.message)
is CacheEffect.NotifyBookChanged -> Unit is BookshelfManageScreenEffect.NotifyBookChanged -> Unit
is CacheEffect.OpenBookInfo -> context.startActivity<BookInfoActivity> { is BookshelfManageScreenEffect.OpenBookInfo -> context.startActivity<BookInfoActivity> {
putExtra("name", effect.name) putExtra("name", effect.name)
putExtra("author", effect.author) putExtra("author", effect.author)
putExtra("bookUrl", effect.bookUrl) putExtra("bookUrl", effect.bookUrl)
@@ -398,20 +400,20 @@ private fun CacheScreen(
return if (targetBooks.all { it.group == firstGroup }) firstGroup else 0L return if (targetBooks.all { it.group == firstGroup }) firstGroup else 0L
} }
val fabItems = listOf( val fabItems = listOf(
CacheFabAction( BookshelfManageFabAction(
Icons.Default.SelectAll, Icons.Default.SelectAll,
stringResource(R.string.select_all) stringResource(R.string.select_all)
) { ) {
selectedBookUrls = state.books.mapTo(hashSetOf()) { it.bookUrl } selectedBookUrls = state.books.mapTo(hashSetOf()) { it.bookUrl }
}, },
CacheFabAction( BookshelfManageFabAction(
Icons.Default.Refresh, Icons.Default.Refresh,
stringResource(R.string.revert_selection) stringResource(R.string.revert_selection)
) { ) {
val visibleBookUrls = booksByUrl.keys val visibleBookUrls = booksByUrl.keys
selectedBookUrls = visibleBookUrls - selectedBookUrls selectedBookUrls = visibleBookUrls - selectedBookUrls
}, },
CacheFabAction( BookshelfManageFabAction(
Icons.Default.Download, Icons.Default.Download,
"缓存选中" "缓存选中"
) { ) {
@@ -419,7 +421,7 @@ private fun CacheScreen(
showBatchDownloadConfirmDialog = true showBatchDownloadConfirmDialog = true
} }
}, },
CacheFabAction( BookshelfManageFabAction(
Icons.Default.Refresh, Icons.Default.Refresh,
"批量换源" "批量换源"
) { ) {
@@ -427,7 +429,7 @@ private fun CacheScreen(
showBatchSourcePickerSheet = true showBatchSourcePickerSheet = true
} }
}, },
CacheFabAction( BookshelfManageFabAction(
Icons.Default.Bookmarks, Icons.Default.Bookmarks,
stringResource(R.string.move_to_group) stringResource(R.string.move_to_group)
) { ) {
@@ -437,20 +439,20 @@ private fun CacheScreen(
showGroupSelectSheet = true showGroupSelectSheet = true
} }
}, },
CacheFabAction( BookshelfManageFabAction(
Icons.Default.Upload, Icons.Default.Upload,
"导出选中" "导出选中"
) { ) {
exportSelected() exportSelected()
}, },
CacheFabAction( BookshelfManageFabAction(
Icons.Default.Delete, Icons.Default.Delete,
stringResource(R.string.clear_cache) stringResource(R.string.clear_cache)
) { ) {
viewModel.dispatch(CacheIntent.ClearCachesForBooks(selectedBookUrls)) viewModel.dispatch(BookshelfManageScreenIntent.ClearCachesForBooks(selectedBookUrls))
clearSelection() clearSelection()
}, },
CacheFabAction( BookshelfManageFabAction(
Icons.Default.Delete, Icons.Default.Delete,
stringResource(R.string.delete) stringResource(R.string.delete)
) { ) {
@@ -462,6 +464,17 @@ private fun CacheScreen(
} }
) )
val listState = rememberLazyListState() val listState = rememberLazyListState()
val canReorderBooks = state.bookSort == 3 && !isSearchMode && selectedBookUrls.isEmpty()
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
if (canReorderBooks) {
viewModel.dispatch(
BookshelfManageScreenIntent.MoveBookOrder(
fromIndex = from.index,
toIndex = to.index
)
)
}
}
ListScaffold( ListScaffold(
title = if (inSelectionMode) { title = if (inSelectionMode) {
"已选 ${selectedBookUrls.size}/${filteredBooks.size}" "已选 ${selectedBookUrls.size}/${filteredBooks.size}"
@@ -495,7 +508,7 @@ private fun CacheScreen(
isSelected = group.groupId == state.groupId, isSelected = group.groupId == state.groupId,
onClick = { onClick = {
dismiss() dismiss()
viewModel.dispatch(CacheIntent.ChangeGroup(group.groupId)) viewModel.dispatch(BookshelfManageScreenIntent.ChangeGroup(group.groupId))
} }
) )
} }
@@ -508,7 +521,7 @@ private fun CacheScreen(
onClick = { onClick = {
dismiss() dismiss()
if (state.isDownloadRunning) { if (state.isDownloadRunning) {
viewModel.dispatch(CacheIntent.StopDownload) viewModel.dispatch(BookshelfManageScreenIntent.StopDownload)
} else { } else {
showDownloadAllConfirmDialog = true showDownloadAllConfirmDialog = true
} }
@@ -553,7 +566,7 @@ private fun CacheScreen(
onClick = { onClick = {
dismiss() dismiss()
viewModel.dispatch( viewModel.dispatch(
CacheIntent.SetExportUseReplace(!state.exportConfig.exportUseReplace) BookshelfManageScreenIntent.SetExportUseReplace(!state.exportConfig.exportUseReplace)
) )
} }
) )
@@ -563,7 +576,7 @@ private fun CacheScreen(
onClick = { onClick = {
dismiss() dismiss()
viewModel.dispatch( viewModel.dispatch(
CacheIntent.SetEnableCustomExport(!state.exportConfig.enableCustomExport) BookshelfManageScreenIntent.SetEnableCustomExport(!state.exportConfig.enableCustomExport)
) )
} }
) )
@@ -573,7 +586,7 @@ private fun CacheScreen(
onClick = { onClick = {
dismiss() dismiss()
viewModel.dispatch( viewModel.dispatch(
CacheIntent.SetExportNoChapterName(!state.exportConfig.exportNoChapterName) BookshelfManageScreenIntent.SetExportNoChapterName(!state.exportConfig.exportNoChapterName)
) )
} }
) )
@@ -583,7 +596,7 @@ private fun CacheScreen(
onClick = { onClick = {
dismiss() dismiss()
viewModel.dispatch( viewModel.dispatch(
CacheIntent.SetExportToWebDav(!state.exportConfig.exportToWebDav) BookshelfManageScreenIntent.SetExportToWebDav(!state.exportConfig.exportToWebDav)
) )
} }
) )
@@ -593,7 +606,7 @@ private fun CacheScreen(
onClick = { onClick = {
dismiss() dismiss()
viewModel.dispatch( viewModel.dispatch(
CacheIntent.SetExportPictureFile(!state.exportConfig.exportPictureFile) BookshelfManageScreenIntent.SetExportPictureFile(!state.exportConfig.exportPictureFile)
) )
} }
) )
@@ -603,7 +616,7 @@ private fun CacheScreen(
onClick = { onClick = {
dismiss() dismiss()
viewModel.dispatch( viewModel.dispatch(
CacheIntent.SetParallelExportBook(!state.exportConfig.parallelExportBook) BookshelfManageScreenIntent.SetParallelExportBook(!state.exportConfig.parallelExportBook)
) )
} }
) )
@@ -680,8 +693,21 @@ private fun CacheScreen(
val exportMsg = remember(renderVersion, book.bookUrl) { val exportMsg = remember(renderVersion, book.bookUrl) {
ExportBookService.exportMsg[book.bookUrl] ExportBookService.exportMsg[book.bookUrl]
} }
ReorderableItem(
state = reorderableState,
key = book.bookUrl,
enabled = canReorderBooks
) {
NormalCard( NormalCard(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.fillMaxWidth()
.then(
if (canReorderBooks) {
Modifier.longPressDraggableHandle()
} else {
Modifier
}
),
onClick = { toggleBookSelection(book) }, onClick = { toggleBookSelection(book) },
onLongClick = { toggleBookSelection(book) }, onLongClick = { toggleBookSelection(book) },
containerColor = if (isSelected) { containerColor = if (isSelected) {
@@ -741,7 +767,7 @@ private fun CacheScreen(
SmallTonalIconButton( SmallTonalIconButton(
onClick = { onClick = {
if (!book.isLocal) { if (!book.isLocal) {
viewModel.dispatch(CacheIntent.ToggleBookDownload(book)) viewModel.dispatch(BookshelfManageScreenIntent.ToggleBookDownload(book))
} }
}, },
imageVector = if (isDownloading) Icons.Default.Stop else Icons.Default.Download, imageVector = if (isDownloading) Icons.Default.Stop else Icons.Default.Download,
@@ -789,7 +815,11 @@ private fun CacheScreen(
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "删除缓存", text = "删除缓存",
onClick = { onClick = {
viewModel.dispatch(CacheIntent.ClearCachesForBooks(setOf(book.bookUrl))) viewModel.dispatch(
BookshelfManageScreenIntent.ClearCachesForBooks(
setOf(book.bookUrl)
)
)
dismiss() dismiss()
} }
) )
@@ -801,6 +831,7 @@ private fun CacheScreen(
} }
} }
} }
}
singleChangeSourceBook?.let { book -> singleChangeSourceBook?.let { book ->
ChangeSourceSheet( ChangeSourceSheet(
@@ -809,7 +840,7 @@ private fun CacheScreen(
onDismissRequest = { singleChangeSourceBook = null }, onDismissRequest = { singleChangeSourceBook = null },
onReplace = { source, newBook, toc, options -> onReplace = { source, newBook, toc, options ->
viewModel.dispatch( viewModel.dispatch(
CacheIntent.ChangeBookSource( BookshelfManageScreenIntent.ChangeBookSource(
oldBookUrl = book.bookUrl, oldBookUrl = book.bookUrl,
source = source, source = source,
book = newBook, book = newBook,
@@ -842,7 +873,7 @@ private fun CacheScreen(
onDismissRequest = { pendingBatchSources = emptyList() }, onDismissRequest = { pendingBatchSources = emptyList() },
onConfirm = { options -> onConfirm = { options ->
viewModel.dispatch( viewModel.dispatch(
CacheIntent.BatchChangeBookSource( BookshelfManageScreenIntent.BatchChangeBookSource(
bookUrls = selectedBookUrls, bookUrls = selectedBookUrls,
sources = pendingBatchSources, sources = pendingBatchSources,
options = options, options = options,
@@ -856,18 +887,18 @@ private fun CacheScreen(
BatchChangePreviewSheet( BatchChangePreviewSheet(
show = state.batchChangePreviewItems.isNotEmpty(), show = state.batchChangePreviewItems.isNotEmpty(),
items = state.batchChangePreviewItems, items = state.batchChangePreviewItems,
onDismissRequest = { viewModel.dispatch(CacheIntent.DismissBatchChangePreview) }, onDismissRequest = { viewModel.dispatch(BookshelfManageScreenIntent.DismissBatchChangePreview) },
onOpenBook = { book, inBookshelf -> onOpenBook = { book, inBookshelf ->
viewModel.dispatch(CacheIntent.OpenBookInfoPreview(book, inBookshelf)) viewModel.dispatch(BookshelfManageScreenIntent.OpenBookInfoPreview(book, inBookshelf))
}, },
onManualSearch = { book -> manualSearchPreviewBook = book }, onManualSearch = { book -> manualSearchPreviewBook = book },
onSkip = { bookUrl -> viewModel.dispatch(CacheIntent.SkipPreviewItem(bookUrl)) }, onSkip = { bookUrl -> viewModel.dispatch(BookshelfManageScreenIntent.SkipPreviewItem(bookUrl)) },
onMigrate = { bookUrl -> viewModel.dispatch(CacheIntent.MigratePreviewItem(bookUrl)) }, onMigrate = { bookUrl -> viewModel.dispatch(BookshelfManageScreenIntent.MigratePreviewItem(bookUrl)) },
onAddToShelf = { bookUrl -> onAddToShelf = { bookUrl ->
viewModel.dispatch(CacheIntent.AddPreviewItemToShelf(bookUrl)) viewModel.dispatch(BookshelfManageScreenIntent.AddPreviewItemToShelf(bookUrl))
}, },
onShowOtherSources = { item -> otherSourcePreviewItem = item }, onShowOtherSources = { item -> otherSourcePreviewItem = item },
onMigrateAll = { viewModel.dispatch(CacheIntent.MigrateAllPreviewItems) }, onMigrateAll = { viewModel.dispatch(BookshelfManageScreenIntent.MigrateAllPreviewItems) },
) )
manualSearchPreviewBook?.let { book -> manualSearchPreviewBook?.let { book ->
@@ -877,7 +908,7 @@ private fun CacheScreen(
onDismissRequest = { manualSearchPreviewBook = null }, onDismissRequest = { manualSearchPreviewBook = null },
onReplace = { source, newBook, toc, _ -> onReplace = { source, newBook, toc, _ ->
viewModel.dispatch( viewModel.dispatch(
CacheIntent.UpdatePreviewItem( BookshelfManageScreenIntent.UpdatePreviewItem(
oldBookUrl = book.bookUrl, oldBookUrl = book.bookUrl,
source = source, source = source,
book = newBook, book = newBook,
@@ -896,11 +927,11 @@ private fun CacheScreen(
item = otherSourcePreviewItem, item = otherSourcePreviewItem,
onDismissRequest = { otherSourcePreviewItem = null }, onDismissRequest = { otherSourcePreviewItem = null },
onSelect = { oldBookUrl, index -> onSelect = { oldBookUrl, index ->
viewModel.dispatch(CacheIntent.SelectPreviewCandidate(oldBookUrl, index)) viewModel.dispatch(BookshelfManageScreenIntent.SelectPreviewCandidate(oldBookUrl, index))
otherSourcePreviewItem = null otherSourcePreviewItem = null
}, },
onOpenBook = { book -> onOpenBook = { book ->
viewModel.dispatch(CacheIntent.OpenBookInfoPreview(book, false)) viewModel.dispatch(BookshelfManageScreenIntent.OpenBookInfoPreview(book, false))
} }
) )
@@ -908,7 +939,7 @@ private fun CacheScreen(
show = state.isChangingSource || state.changeSourceError != null, show = state.isChangingSource || state.changeSourceError != null,
onDismissRequest = { onDismissRequest = {
if (!state.isChangingSource) { if (!state.isChangingSource) {
viewModel.dispatch(CacheIntent.DismissChangeSourceStatus) viewModel.dispatch(BookshelfManageScreenIntent.DismissChangeSourceStatus)
} }
}, },
title = stringResource(R.string.change_source_batch), title = stringResource(R.string.change_source_batch),
@@ -936,7 +967,7 @@ private fun CacheScreen(
onConfirm = if (state.isChangingSource) { onConfirm = if (state.isChangingSource) {
null null
} else { } else {
{ viewModel.dispatch(CacheIntent.DismissChangeSourceStatus) } { viewModel.dispatch(BookshelfManageScreenIntent.DismissChangeSourceStatus) }
}, },
) )
@@ -959,7 +990,7 @@ private fun CacheScreen(
onConfirm = { onConfirm = {
showBatchDownloadConfirmDialog = false showBatchDownloadConfirmDialog = false
viewModel.dispatch( viewModel.dispatch(
CacheIntent.DownloadBooks( BookshelfManageScreenIntent.DownloadBooks(
bookUrls = selectedBookUrls, bookUrls = selectedBookUrls,
downloadAllChapters = false downloadAllChapters = false
) )
@@ -999,7 +1030,7 @@ private fun CacheScreen(
onConfirm = { onConfirm = {
showDeleteBookConfirmDialog = false showDeleteBookConfirmDialog = false
viewModel.dispatch( viewModel.dispatch(
CacheIntent.DeleteBooks( BookshelfManageScreenIntent.DeleteBooks(
bookUrls = pendingDeleteBookUrls, bookUrls = pendingDeleteBookUrls,
deleteOriginal = deleteOriginalBookFile deleteOriginal = deleteOriginalBookFile
) )
@@ -1019,7 +1050,7 @@ private fun CacheScreen(
val moveSet = pendingMoveGroupBookUrl?.let { setOf(it) } ?: selectedBookUrls val moveSet = pendingMoveGroupBookUrl?.let { setOf(it) } ?: selectedBookUrls
val targetGroupId = groupId.coerceAtLeast(0L) val targetGroupId = groupId.coerceAtLeast(0L)
viewModel.dispatch( viewModel.dispatch(
CacheIntent.MoveBooksToGroup( BookshelfManageScreenIntent.MoveBooksToGroup(
bookUrls = moveSet, bookUrls = moveSet,
groupId = targetGroupId groupId = targetGroupId
) )
@@ -1040,7 +1071,7 @@ private fun CacheScreen(
onConfirm = { onConfirm = {
showDownloadAllConfirmDialog = false showDownloadAllConfirmDialog = false
viewModel.dispatch( viewModel.dispatch(
CacheIntent.StartDownloadForVisibleBooks( BookshelfManageScreenIntent.StartDownloadForVisibleBooks(
books = state.books, books = state.books,
downloadAllChapters = true downloadAllChapters = true
) )
@@ -1060,7 +1091,7 @@ private fun CacheScreen(
icon = if (type == "epub") Icons.Default.Upload else Icons.Default.Download, icon = if (type == "epub") Icons.Default.Upload else Icons.Default.Download,
text = type, text = type,
onClick = { onClick = {
viewModel.dispatch(CacheIntent.SetExportType(index)) viewModel.dispatch(BookshelfManageScreenIntent.SetExportType(index))
showExportTypeDialog = false showExportTypeDialog = false
} }
) )
@@ -1085,7 +1116,7 @@ private fun CacheScreen(
}, },
confirmText = stringResource(android.R.string.ok), confirmText = stringResource(android.R.string.ok),
onConfirm = { onConfirm = {
viewModel.dispatch(CacheIntent.SetBookExportFileName(exportFileNameInput)) viewModel.dispatch(BookshelfManageScreenIntent.SetBookExportFileName(exportFileNameInput))
showExportFileNameDialog = false showExportFileNameDialog = false
}, },
dismissText = stringResource(android.R.string.cancel), dismissText = stringResource(android.R.string.cancel),
@@ -1107,7 +1138,7 @@ private fun CacheScreen(
confirmText = stringResource(android.R.string.ok), confirmText = stringResource(android.R.string.ok),
onConfirm = { onConfirm = {
viewModel.dispatch( viewModel.dispatch(
CacheIntent.SetExportCharset( BookshelfManageScreenIntent.SetExportCharset(
exportCharsetInput.ifBlank { "UTF-8" } exportCharsetInput.ifBlank { "UTF-8" }
) )
) )
@@ -1228,7 +1259,7 @@ private fun CacheScreen(
customEpisodeExportNameInput customEpisodeExportNameInput
) )
) { ) {
viewModel.dispatch(CacheIntent.SetEpisodeExportFileName(customEpisodeExportNameInput)) viewModel.dispatch(BookshelfManageScreenIntent.SetEpisodeExportFileName(customEpisodeExportNameInput))
} }
val epubSize = customEpubSizeInput.toIntOrNull()?.coerceAtLeast(1) ?: 1 val epubSize = customEpubSizeInput.toIntOrNull()?.coerceAtLeast(1) ?: 1
context.startService<ExportBookService> { context.startService<ExportBookService> {
@@ -1,4 +1,4 @@
package io.legado.app.ui.book.cache package io.legado.app.ui.book.manage
import android.app.Application import android.app.Application
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@@ -29,10 +29,12 @@ import io.legado.app.help.book.removeType
import io.legado.app.model.CacheBook import io.legado.app.model.CacheBook
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.service.ExportBookService import io.legado.app.service.ExportBookService
import io.legado.app.ui.config.cacheConfig.CacheConfig import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
import io.legado.app.ui.config.bookshelfConfig.BookshelfManageScreenConfig
import io.legado.app.ui.config.otherConfig.OtherConfig import io.legado.app.ui.config.otherConfig.OtherConfig
import io.legado.app.ui.main.bookshelf.toLightBook import io.legado.app.ui.main.bookshelf.toLightBook
import io.legado.app.utils.cnCompare import io.legado.app.utils.cnCompare
import io.legado.app.utils.move
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@@ -44,7 +46,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.max import kotlin.math.max
data class CacheExportConfig( data class BookshelfManageScreenExportConfig(
val exportUseReplace: Boolean = true, val exportUseReplace: Boolean = true,
val enableCustomExport: Boolean = false, val enableCustomExport: Boolean = false,
val exportNoChapterName: Boolean = false, val exportNoChapterName: Boolean = false,
@@ -57,11 +59,13 @@ data class CacheExportConfig(
val episodeExportFileName: String = "" val episodeExportFileName: String = ""
) )
data class CacheUiState( data class BookshelfManageScreenUiState(
val groupId: Long = -1, val groupId: Long = -1,
val groupName: String? = null, val groupName: String? = null,
val groupList: List<BookGroup> = emptyList(), val groupList: List<BookGroup> = emptyList(),
val books: List<Book> = emptyList(), val books: List<Book> = emptyList(),
val bookSort: Int = BookshelfConfig.bookshelfSort,
val bookSortOrder: Int = BookshelfConfig.bookshelfSortOrder,
val isDownloadRunning: Boolean = false, val isDownloadRunning: Boolean = false,
val isChangingSource: Boolean = false, val isChangingSource: Boolean = false,
val changeSourceProgress: String? = null, val changeSourceProgress: String? = null,
@@ -71,74 +75,75 @@ data class CacheUiState(
val batchChangeOptions: ChangeSourceMigrationOptions = ChangeSourceMigrationOptions(), val batchChangeOptions: ChangeSourceMigrationOptions = ChangeSourceMigrationOptions(),
val cacheVersion: Long = 0, val cacheVersion: Long = 0,
val deleteBookOriginal: Boolean = LocalConfig.deleteBookOriginal, val deleteBookOriginal: Boolean = LocalConfig.deleteBookOriginal,
val exportConfig: CacheExportConfig = CacheExportConfig() val exportConfig: BookshelfManageScreenExportConfig = BookshelfManageScreenExportConfig()
) )
sealed interface CacheIntent { sealed interface BookshelfManageScreenIntent {
data class Initialize(val groupId: Long) : CacheIntent data class Initialize(val groupId: Long) : BookshelfManageScreenIntent
data class ChangeGroup(val groupId: Long) : CacheIntent data class ChangeGroup(val groupId: Long) : BookshelfManageScreenIntent
data class StartDownloadForVisibleBooks( data class StartDownloadForVisibleBooks(
val books: List<Book>, val books: List<Book>,
val downloadAllChapters: Boolean val downloadAllChapters: Boolean
) : CacheIntent ) : BookshelfManageScreenIntent
data object StopDownload : CacheIntent data object StopDownload : BookshelfManageScreenIntent
data class ToggleBookDownload(val book: Book) : CacheIntent data class ToggleBookDownload(val book: Book) : BookshelfManageScreenIntent
data class DeleteBookDownload(val bookUrl: String) : CacheIntent data class DeleteBookDownload(val bookUrl: String) : BookshelfManageScreenIntent
data class ClearBookCache(val book: Book) : CacheIntent data class ClearBookCache(val book: Book) : BookshelfManageScreenIntent
data class MoveBooksToGroup(val bookUrls: Set<String>, val groupId: Long) : CacheIntent data class MoveBooksToGroup(val bookUrls: Set<String>, val groupId: Long) : BookshelfManageScreenIntent
data class DeleteBooks(val bookUrls: Set<String>, val deleteOriginal: Boolean) : CacheIntent data class DeleteBooks(val bookUrls: Set<String>, val deleteOriginal: Boolean) : BookshelfManageScreenIntent
data class ClearCachesForBooks(val bookUrls: Set<String>) : CacheIntent data class ClearCachesForBooks(val bookUrls: Set<String>) : BookshelfManageScreenIntent
data class DownloadBooks(val bookUrls: Set<String>, val downloadAllChapters: Boolean) : CacheIntent data class MoveBookOrder(val fromIndex: Int, val toIndex: Int) : BookshelfManageScreenIntent
data class DownloadBooks(val bookUrls: Set<String>, val downloadAllChapters: Boolean) : BookshelfManageScreenIntent
data class ChangeBookSource( data class ChangeBookSource(
val oldBookUrl: String, val oldBookUrl: String,
val source: BookSource, val source: BookSource,
val book: Book, val book: Book,
val chapters: List<BookChapter>, val chapters: List<BookChapter>,
val options: ChangeSourceMigrationOptions, val options: ChangeSourceMigrationOptions,
) : CacheIntent ) : BookshelfManageScreenIntent
data class BatchChangeBookSource( data class BatchChangeBookSource(
val bookUrls: Set<String>, val bookUrls: Set<String>,
val sources: List<BookSource>, val sources: List<BookSource>,
val options: ChangeSourceMigrationOptions, val options: ChangeSourceMigrationOptions,
) : CacheIntent ) : BookshelfManageScreenIntent
data class MigratePreviewItem(val oldBookUrl: String) : CacheIntent data class MigratePreviewItem(val oldBookUrl: String) : BookshelfManageScreenIntent
data class SkipPreviewItem(val oldBookUrl: String) : CacheIntent data class SkipPreviewItem(val oldBookUrl: String) : BookshelfManageScreenIntent
data class SelectPreviewCandidate(val oldBookUrl: String, val candidateIndex: Int) : CacheIntent data class SelectPreviewCandidate(val oldBookUrl: String, val candidateIndex: Int) : BookshelfManageScreenIntent
data class UpdatePreviewItem( data class UpdatePreviewItem(
val oldBookUrl: String, val oldBookUrl: String,
val source: BookSource, val source: BookSource,
val book: Book, val book: Book,
val chapters: List<BookChapter>, val chapters: List<BookChapter>,
) : CacheIntent ) : BookshelfManageScreenIntent
data class AddPreviewItemToShelf(val oldBookUrl: String) : CacheIntent data class AddPreviewItemToShelf(val oldBookUrl: String) : BookshelfManageScreenIntent
data class OpenBookInfoPreview(val book: Book, val inBookshelf: Boolean) : CacheIntent data class OpenBookInfoPreview(val book: Book, val inBookshelf: Boolean) : BookshelfManageScreenIntent
data object MigrateAllPreviewItems : CacheIntent data object MigrateAllPreviewItems : BookshelfManageScreenIntent
data object DismissChangeSourceStatus : CacheIntent data object DismissChangeSourceStatus : BookshelfManageScreenIntent
data object DismissBatchChangePreview : CacheIntent data object DismissBatchChangePreview : BookshelfManageScreenIntent
data class SetExportUseReplace(val enabled: Boolean) : CacheIntent data class SetExportUseReplace(val enabled: Boolean) : BookshelfManageScreenIntent
data class SetEnableCustomExport(val enabled: Boolean) : CacheIntent data class SetEnableCustomExport(val enabled: Boolean) : BookshelfManageScreenIntent
data class SetExportNoChapterName(val enabled: Boolean) : CacheIntent data class SetExportNoChapterName(val enabled: Boolean) : BookshelfManageScreenIntent
data class SetExportToWebDav(val enabled: Boolean) : CacheIntent data class SetExportToWebDav(val enabled: Boolean) : BookshelfManageScreenIntent
data class SetExportPictureFile(val enabled: Boolean) : CacheIntent data class SetExportPictureFile(val enabled: Boolean) : BookshelfManageScreenIntent
data class SetParallelExportBook(val enabled: Boolean) : CacheIntent data class SetParallelExportBook(val enabled: Boolean) : BookshelfManageScreenIntent
data class SetExportType(val type: Int) : CacheIntent data class SetExportType(val type: Int) : BookshelfManageScreenIntent
data class SetExportCharset(val charset: String) : CacheIntent data class SetExportCharset(val charset: String) : BookshelfManageScreenIntent
data class SetBookExportFileName(val fileName: String?) : CacheIntent data class SetBookExportFileName(val fileName: String?) : BookshelfManageScreenIntent
data class SetEpisodeExportFileName(val fileName: String) : CacheIntent data class SetEpisodeExportFileName(val fileName: String) : BookshelfManageScreenIntent
} }
sealed interface CacheEffect { sealed interface BookshelfManageScreenEffect {
data class NotifyBookChanged(val bookUrl: String) : CacheEffect data class NotifyBookChanged(val bookUrl: String) : BookshelfManageScreenEffect
data class ShowMessage(val message: String) : CacheEffect data class ShowMessage(val message: String) : BookshelfManageScreenEffect
data class OpenBookInfo(val bookUrl: String, val name: String, val author: String) : CacheEffect data class OpenBookInfo(val bookUrl: String, val name: String, val author: String) : BookshelfManageScreenEffect
} }
class CacheViewModel( class BookshelfManageScreenViewModel(
application: Application, application: Application,
private val bookDao: BookDao, private val bookDao: BookDao,
private val bookGroupDao: BookGroupDao, private val bookGroupDao: BookGroupDao,
private val bookChapterDao: BookChapterDao, private val bookChapterDao: BookChapterDao,
val cacheConfig: CacheConfig, val bookshelfManageScreenConfig: BookshelfManageScreenConfig,
private val batchCacheDownloadUseCase: BatchCacheDownloadUseCase, private val batchCacheDownloadUseCase: BatchCacheDownloadUseCase,
private val cacheBookChaptersUseCase: CacheBookChaptersUseCase, private val cacheBookChaptersUseCase: CacheBookChaptersUseCase,
private val changeBookSourceUseCase: ChangeBookSourceUseCase, private val changeBookSourceUseCase: ChangeBookSourceUseCase,
@@ -147,10 +152,10 @@ class CacheViewModel(
private val updateBooksGroupUseCase: UpdateBooksGroupUseCase private val updateBooksGroupUseCase: UpdateBooksGroupUseCase
) : BaseViewModel(application) { ) : BaseViewModel(application) {
private val _uiState = MutableStateFlow(CacheUiState()) private val _uiState = MutableStateFlow(BookshelfManageScreenUiState())
val uiState = _uiState.asStateFlow() val uiState = _uiState.asStateFlow()
private val _effects = MutableSharedFlow<CacheEffect>(extraBufferCapacity = 32) private val _effects = MutableSharedFlow<BookshelfManageScreenEffect>(extraBufferCapacity = 32)
val effects = _effects.asSharedFlow() val effects = _effects.asSharedFlow()
private val cacheChapters = hashMapOf<String, HashSet<String>>() private val cacheChapters = hashMapOf<String, HashSet<String>>()
@@ -158,24 +163,25 @@ class CacheViewModel(
private var groupsJob: Job? = null private var groupsJob: Job? = null
private var observersStarted = false private var observersStarted = false
fun dispatch(intent: CacheIntent) { fun dispatch(intent: BookshelfManageScreenIntent) {
when (intent) { when (intent) {
is CacheIntent.Initialize -> initialize(intent.groupId) is BookshelfManageScreenIntent.Initialize -> initialize(intent.groupId)
is CacheIntent.ChangeGroup -> changeGroup(intent.groupId) is BookshelfManageScreenIntent.ChangeGroup -> changeGroup(intent.groupId)
is CacheIntent.StartDownloadForVisibleBooks -> startDownloadForVisibleBooks( is BookshelfManageScreenIntent.StartDownloadForVisibleBooks -> startDownloadForVisibleBooks(
intent.books, intent.books,
intent.downloadAllChapters intent.downloadAllChapters
) )
CacheIntent.StopDownload -> CacheBook.stop(context) BookshelfManageScreenIntent.StopDownload -> CacheBook.stop(context)
is CacheIntent.ToggleBookDownload -> toggleBookDownload(intent.book) is BookshelfManageScreenIntent.ToggleBookDownload -> toggleBookDownload(intent.book)
is CacheIntent.DeleteBookDownload -> CacheBook.remove(context, intent.bookUrl) is BookshelfManageScreenIntent.DeleteBookDownload -> CacheBook.remove(context, intent.bookUrl)
is CacheIntent.ClearBookCache -> clearCacheForBook(intent.book) is BookshelfManageScreenIntent.ClearBookCache -> clearCacheForBook(intent.book)
is CacheIntent.MoveBooksToGroup -> moveBooksToGroup(intent.bookUrls, intent.groupId) is BookshelfManageScreenIntent.MoveBooksToGroup -> moveBooksToGroup(intent.bookUrls, intent.groupId)
is CacheIntent.DeleteBooks -> deleteBooks(intent.bookUrls, intent.deleteOriginal) is BookshelfManageScreenIntent.DeleteBooks -> deleteBooks(intent.bookUrls, intent.deleteOriginal)
is CacheIntent.ClearCachesForBooks -> clearCachesForBooks(intent.bookUrls) is BookshelfManageScreenIntent.ClearCachesForBooks -> clearCachesForBooks(intent.bookUrls)
is CacheIntent.DownloadBooks -> downloadBooks(intent.bookUrls, intent.downloadAllChapters) is BookshelfManageScreenIntent.MoveBookOrder -> moveBookOrder(intent.fromIndex, intent.toIndex)
is CacheIntent.ChangeBookSource -> changeBookSource( is BookshelfManageScreenIntent.DownloadBooks -> downloadBooks(intent.bookUrls, intent.downloadAllChapters)
is BookshelfManageScreenIntent.ChangeBookSource -> changeBookSource(
intent.oldBookUrl, intent.oldBookUrl,
intent.source, intent.source,
intent.book, intent.book,
@@ -183,35 +189,35 @@ class CacheViewModel(
intent.options intent.options
) )
is CacheIntent.BatchChangeBookSource -> batchChangeBookSource( is BookshelfManageScreenIntent.BatchChangeBookSource -> batchChangeBookSource(
intent.bookUrls, intent.bookUrls,
intent.sources, intent.sources,
intent.options intent.options
) )
is CacheIntent.MigratePreviewItem -> migratePreviewItem(intent.oldBookUrl) is BookshelfManageScreenIntent.MigratePreviewItem -> migratePreviewItem(intent.oldBookUrl)
is CacheIntent.SkipPreviewItem -> skipPreviewItem(intent.oldBookUrl) is BookshelfManageScreenIntent.SkipPreviewItem -> skipPreviewItem(intent.oldBookUrl)
is CacheIntent.SelectPreviewCandidate -> selectPreviewCandidate( is BookshelfManageScreenIntent.SelectPreviewCandidate -> selectPreviewCandidate(
intent.oldBookUrl, intent.oldBookUrl,
intent.candidateIndex intent.candidateIndex
) )
is CacheIntent.UpdatePreviewItem -> updatePreviewItem( is BookshelfManageScreenIntent.UpdatePreviewItem -> updatePreviewItem(
intent.oldBookUrl, intent.oldBookUrl,
intent.source, intent.source,
intent.book, intent.book,
intent.chapters intent.chapters
) )
is CacheIntent.AddPreviewItemToShelf -> addPreviewItemToShelf(intent.oldBookUrl) is BookshelfManageScreenIntent.AddPreviewItemToShelf -> addPreviewItemToShelf(intent.oldBookUrl)
is CacheIntent.OpenBookInfoPreview -> openBookInfoPreview( is BookshelfManageScreenIntent.OpenBookInfoPreview -> openBookInfoPreview(
intent.book, intent.book,
intent.inBookshelf intent.inBookshelf
) )
CacheIntent.MigrateAllPreviewItems -> migrateAllPreviewItems() BookshelfManageScreenIntent.MigrateAllPreviewItems -> migrateAllPreviewItems()
CacheIntent.DismissChangeSourceStatus -> { BookshelfManageScreenIntent.DismissChangeSourceStatus -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
changeSourceProgress = null, changeSourceProgress = null,
@@ -221,59 +227,59 @@ class CacheViewModel(
} }
} }
CacheIntent.DismissBatchChangePreview -> { BookshelfManageScreenIntent.DismissBatchChangePreview -> {
_uiState.update { it.copy(batchChangePreviewItems = emptyList()) } _uiState.update { it.copy(batchChangePreviewItems = emptyList()) }
} }
is CacheIntent.SetExportUseReplace -> { is BookshelfManageScreenIntent.SetExportUseReplace -> {
cacheConfig.exportUseReplace = intent.enabled bookshelfManageScreenConfig.exportUseReplace = intent.enabled
syncExportConfig() syncExportConfig()
val msg = if (intent.enabled) "替换净化功能已开启" else "替换净化功能已关闭" val msg = if (intent.enabled) "替换净化功能已开启" else "替换净化功能已关闭"
_effects.tryEmit(CacheEffect.ShowMessage(msg)) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage(msg))
} }
is CacheIntent.SetEnableCustomExport -> { is BookshelfManageScreenIntent.SetEnableCustomExport -> {
cacheConfig.enableCustomExport = intent.enabled bookshelfManageScreenConfig.enableCustomExport = intent.enabled
syncExportConfig() syncExportConfig()
} }
is CacheIntent.SetExportNoChapterName -> { is BookshelfManageScreenIntent.SetExportNoChapterName -> {
cacheConfig.exportNoChapterName = intent.enabled bookshelfManageScreenConfig.exportNoChapterName = intent.enabled
syncExportConfig() syncExportConfig()
} }
is CacheIntent.SetExportToWebDav -> { is BookshelfManageScreenIntent.SetExportToWebDav -> {
cacheConfig.exportToWebDav = intent.enabled bookshelfManageScreenConfig.exportToWebDav = intent.enabled
syncExportConfig() syncExportConfig()
} }
is CacheIntent.SetExportPictureFile -> { is BookshelfManageScreenIntent.SetExportPictureFile -> {
cacheConfig.exportPictureFile = intent.enabled bookshelfManageScreenConfig.exportPictureFile = intent.enabled
syncExportConfig() syncExportConfig()
} }
is CacheIntent.SetParallelExportBook -> { is BookshelfManageScreenIntent.SetParallelExportBook -> {
cacheConfig.parallelExportBook = intent.enabled bookshelfManageScreenConfig.parallelExportBook = intent.enabled
syncExportConfig() syncExportConfig()
} }
is CacheIntent.SetExportType -> { is BookshelfManageScreenIntent.SetExportType -> {
cacheConfig.exportType = intent.type bookshelfManageScreenConfig.exportType = intent.type
syncExportConfig() syncExportConfig()
} }
is CacheIntent.SetExportCharset -> { is BookshelfManageScreenIntent.SetExportCharset -> {
cacheConfig.exportCharset = intent.charset bookshelfManageScreenConfig.exportCharset = intent.charset
syncExportConfig() syncExportConfig()
} }
is CacheIntent.SetBookExportFileName -> { is BookshelfManageScreenIntent.SetBookExportFileName -> {
cacheConfig.bookExportFileName = intent.fileName bookshelfManageScreenConfig.bookExportFileName = intent.fileName
syncExportConfig() syncExportConfig()
} }
is CacheIntent.SetEpisodeExportFileName -> { is BookshelfManageScreenIntent.SetEpisodeExportFileName -> {
cacheConfig.episodeExportFileName = intent.fileName bookshelfManageScreenConfig.episodeExportFileName = intent.fileName
syncExportConfig() syncExportConfig()
} }
} }
@@ -316,18 +322,38 @@ class CacheViewModel(
booksJob = viewModelScope.launch { booksJob = viewModelScope.launch {
bookDao.flowBookShelfByGroup(groupId).map { books -> bookDao.flowBookShelfByGroup(groupId).map { books ->
val booksDownload = books.filter { !it.isAudio }.map { it.toLightBook() } val booksDownload = books.filter { !it.isAudio }.map { it.toLightBook() }
when (cacheConfig.getBookSortByGroupId(groupId)) { val bookSort = bookshelfManageScreenConfig.getBookSortByGroupId(groupId)
1 -> booksDownload.sortedByDescending { it.latestChapterTime } val isDescending = bookshelfManageScreenConfig.bookshelfSortOrder == 1
2 -> booksDownload.sortedWith { o1, o2 -> o1.name.cnCompare(o2.name) } bookSort to when (bookSort) {
3 -> booksDownload.sortedBy { it.order } 1 -> if (isDescending) booksDownload.sortedByDescending { it.latestChapterTime }
4 -> booksDownload.sortedByDescending { else booksDownload.sortedBy { it.latestChapterTime }
2 -> if (isDescending) {
booksDownload.sortedWith { o1, o2 -> o2.name.cnCompare(o1.name) }
} else {
booksDownload.sortedWith { o1, o2 -> o1.name.cnCompare(o2.name) }
}
3 -> if (isDescending) booksDownload.sortedByDescending { it.order }
else booksDownload.sortedBy { it.order }
4 -> if (isDescending) booksDownload.sortedByDescending {
max(it.latestChapterTime, it.durChapterTime)
} else booksDownload.sortedBy {
max(it.latestChapterTime, it.durChapterTime) max(it.latestChapterTime, it.durChapterTime)
} }
else -> booksDownload.sortedByDescending { it.durChapterTime } else -> if (isDescending) booksDownload.sortedByDescending { it.durChapterTime }
else booksDownload.sortedBy { it.durChapterTime }
}
}.collect { (bookSort, books) ->
_uiState.update {
it.copy(
books = books,
bookSort = bookSort,
bookSortOrder = bookshelfManageScreenConfig.bookshelfSortOrder
)
} }
}.collect { books ->
_uiState.update { it.copy(books = books) }
loadCacheFiles(books) loadCacheFiles(books)
} }
} }
@@ -372,17 +398,17 @@ class CacheViewModel(
private fun syncExportConfig() { private fun syncExportConfig() {
_uiState.update { _uiState.update {
it.copy( it.copy(
exportConfig = CacheExportConfig( exportConfig = BookshelfManageScreenExportConfig(
exportUseReplace = cacheConfig.exportUseReplace, exportUseReplace = bookshelfManageScreenConfig.exportUseReplace,
enableCustomExport = cacheConfig.enableCustomExport, enableCustomExport = bookshelfManageScreenConfig.enableCustomExport,
exportNoChapterName = cacheConfig.exportNoChapterName, exportNoChapterName = bookshelfManageScreenConfig.exportNoChapterName,
exportToWebDav = cacheConfig.exportToWebDav, exportToWebDav = bookshelfManageScreenConfig.exportToWebDav,
exportPictureFile = cacheConfig.exportPictureFile, exportPictureFile = bookshelfManageScreenConfig.exportPictureFile,
parallelExportBook = cacheConfig.parallelExportBook, parallelExportBook = bookshelfManageScreenConfig.parallelExportBook,
exportType = cacheConfig.exportType, exportType = bookshelfManageScreenConfig.exportType,
exportCharset = cacheConfig.exportCharset, exportCharset = bookshelfManageScreenConfig.exportCharset,
bookExportFileName = cacheConfig.bookExportFileName, bookExportFileName = bookshelfManageScreenConfig.bookExportFileName,
episodeExportFileName = cacheConfig.episodeExportFileName episodeExportFileName = bookshelfManageScreenConfig.episodeExportFileName
) )
) )
} }
@@ -463,7 +489,7 @@ class CacheViewModel(
execute { execute {
updateBooksGroupUseCase.replaceGroup(bookUrls, safeGroupId) updateBooksGroupUseCase.replaceGroup(bookUrls, safeGroupId)
}.onError { }.onError {
_effects.tryEmit(CacheEffect.ShowMessage("移动分组失败\n${it.localizedMessage}")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("移动分组失败\n${it.localizedMessage}"))
} }
} }
@@ -475,9 +501,9 @@ class CacheViewModel(
}.onSuccess { deletedBookUrls -> }.onSuccess { deletedBookUrls ->
_uiState.update { it.copy(deleteBookOriginal = deleteOriginal) } _uiState.update { it.copy(deleteBookOriginal = deleteOriginal) }
deletedBookUrls.forEach { cacheChapters.remove(it) } deletedBookUrls.forEach { cacheChapters.remove(it) }
_effects.tryEmit(CacheEffect.ShowMessage("删除成功")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("删除成功"))
}.onError { }.onError {
_effects.tryEmit(CacheEffect.ShowMessage("删除失败\n${it.localizedMessage}")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("删除失败\n${it.localizedMessage}"))
} }
} }
@@ -490,9 +516,33 @@ class CacheViewModel(
cacheChapters[bookUrl] = hashSetOf() cacheChapters[bookUrl] = hashSetOf()
emitBookChanged(bookUrl) emitBookChanged(bookUrl)
} }
_effects.tryEmit(CacheEffect.ShowMessage("缓存已清理")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("缓存已清理"))
}.onError { }.onError {
_effects.tryEmit(CacheEffect.ShowMessage("清理缓存失败\n${it.localizedMessage}")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("清理缓存失败\n${it.localizedMessage}"))
}
}
private fun moveBookOrder(fromIndex: Int, toIndex: Int) {
val books = uiState.value.books
if (fromIndex !in books.indices || toIndex !in books.indices || fromIndex == toIndex) {
return
}
val reorderedBooks = books.toMutableList().apply { move(fromIndex, toIndex) }
val isDescending = uiState.value.bookSortOrder == 1
val maxOrder = reorderedBooks.size
reorderedBooks.forEachIndexed { index, book ->
book.order = if (isDescending) maxOrder - index else index + 1
}
_uiState.update {
it.copy(
books = reorderedBooks,
cacheVersion = it.cacheVersion + 1
)
}
execute {
bookDao.update(*reorderedBooks.toTypedArray())
}.onError {
_effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("排序保存失败\n${it.localizedMessage}"))
} }
} }
@@ -506,13 +556,13 @@ class CacheViewModel(
) )
}.onSuccess { count -> }.onSuccess { count ->
if (count > 0) { if (count > 0) {
_effects.tryEmit(CacheEffect.ShowMessage("已加入缓存队列: $count")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("已加入缓存队列: $count"))
} else { } else {
_effects.tryEmit(CacheEffect.ShowMessage("没有可缓存的书籍")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("没有可缓存的书籍"))
} }
syncDownloadRunning() syncDownloadRunning()
}.onError { }.onError {
_effects.tryEmit(CacheEffect.ShowMessage("批量缓存失败\n${it.localizedMessage}")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("批量缓存失败\n${it.localizedMessage}"))
} }
} }
@@ -531,9 +581,9 @@ class CacheViewModel(
cacheChapters.remove(result.oldBookUrl) cacheChapters.remove(result.oldBookUrl)
cacheChapters[result.book.bookUrl] = hashSetOf() cacheChapters[result.book.bookUrl] = hashSetOf()
emitBookChanged(result.book.bookUrl) emitBookChanged(result.book.bookUrl)
_effects.tryEmit(CacheEffect.ShowMessage("换源完成")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("换源完成"))
}.onError { }.onError {
_effects.tryEmit(CacheEffect.ShowMessage("换源失败\n${it.localizedMessage}")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("换源失败\n${it.localizedMessage}"))
} }
} }
@@ -626,9 +676,9 @@ class CacheViewModel(
cacheChapters[result.book.bookUrl] = hashSetOf() cacheChapters[result.book.bookUrl] = hashSetOf()
removePreviewItem(oldBookUrl) removePreviewItem(oldBookUrl)
emitBookChanged(result.book.bookUrl) emitBookChanged(result.book.bookUrl)
_effects.tryEmit(CacheEffect.ShowMessage("迁移完成")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("迁移完成"))
}.onError { }.onError {
_effects.tryEmit(CacheEffect.ShowMessage("迁移失败\n${it.localizedMessage}")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("迁移失败\n${it.localizedMessage}"))
} }
} }
@@ -704,9 +754,9 @@ class CacheViewModel(
bookChapterDao.insert(*candidate.chapters.toTypedArray()) bookChapterDao.insert(*candidate.chapters.toTypedArray())
candidate.book candidate.book
}.onSuccess { }.onSuccess {
_effects.tryEmit(CacheEffect.ShowMessage("已添加到书架")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("已添加到书架"))
}.onError { }.onError {
_effects.tryEmit(CacheEffect.ShowMessage("添加书籍失败\n${it.localizedMessage}")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("添加书籍失败\n${it.localizedMessage}"))
} }
} }
@@ -717,7 +767,7 @@ class CacheViewModel(
} }
book book
}.onSuccess { }.onSuccess {
_effects.tryEmit(CacheEffect.OpenBookInfo(it.bookUrl, it.name, it.author)) _effects.tryEmit(BookshelfManageScreenEffect.OpenBookInfo(it.bookUrl, it.name, it.author))
} }
} }
@@ -755,9 +805,9 @@ class CacheViewModel(
}.onSuccess { }.onSuccess {
cacheChapters.clear() cacheChapters.clear()
_uiState.update { it.copy(batchChangePreviewItems = emptyList()) } _uiState.update { it.copy(batchChangePreviewItems = emptyList()) }
_effects.tryEmit(CacheEffect.ShowMessage("批量迁移完成")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("批量迁移完成"))
}.onError { }.onError {
_effects.tryEmit(CacheEffect.ShowMessage("批量迁移失败\n${it.localizedMessage}")) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("批量迁移失败\n${it.localizedMessage}"))
}.onFinally { }.onFinally {
_uiState.update { _uiState.update {
it.copy( it.copy(
@@ -781,7 +831,7 @@ class CacheViewModel(
private fun emitBookChanged(bookUrl: String) { private fun emitBookChanged(bookUrl: String) {
_uiState.update { it.copy(cacheVersion = it.cacheVersion + 1) } _uiState.update { it.copy(cacheVersion = it.cacheVersion + 1) }
_effects.tryEmit(CacheEffect.NotifyBookChanged(bookUrl)) _effects.tryEmit(BookshelfManageScreenEffect.NotifyBookChanged(bookUrl))
} }
} }
@@ -138,6 +138,14 @@ object BookshelfConfig {
*/ */
var bookshelfCoverShadow by prefDelegate(PreferKey.bookshelfCoverShadow, false) var bookshelfCoverShadow by prefDelegate(PreferKey.bookshelfCoverShadow, false)
/**
* 书架搜索按钮是否直接跳转搜索页
*/
var bookshelfSearchActionDirectToSearch by prefDelegate(
PreferKey.bookshelfSearchActionDirectToSearch,
true
)
/** /**
* 启动时自动刷新书架 * 启动时自动刷新书架
*/ */
@@ -1,10 +1,10 @@
package io.legado.app.ui.config.cacheConfig package io.legado.app.ui.config.bookshelfConfig
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.dao.BookGroupDao import io.legado.app.data.dao.BookGroupDao
import io.legado.app.ui.config.prefDelegate import io.legado.app.ui.config.prefDelegate
class CacheConfig( class BookshelfManageScreenConfig(
private val bookGroupDao: BookGroupDao private val bookGroupDao: BookGroupDao
) { ) {
@@ -28,9 +28,10 @@ class CacheConfig(
var parallelExportBook by prefDelegate(PreferKey.parallelExportBook, false) var parallelExportBook by prefDelegate(PreferKey.parallelExportBook, false)
var bookshelfSort by prefDelegate(PreferKey.bookshelfSort, 0)
fun getBookSortByGroupId(groupId: Long): Int { fun getBookSortByGroupId(groupId: Long): Int {
return bookGroupDao.getByID(groupId)?.getRealBookSort() ?: bookshelfSort return bookGroupDao.getByID(groupId)?.getRealBookSort() ?: BookshelfConfig.bookshelfSort
} }
val bookshelfSortOrder: Int
get() = BookshelfConfig.bookshelfSortOrder
} }
@@ -47,7 +47,7 @@ import io.legado.app.ui.book.search.SearchIntent
import io.legado.app.ui.book.search.SearchScreen import io.legado.app.ui.book.search.SearchScreen
import io.legado.app.ui.book.search.SearchViewModel import io.legado.app.ui.book.search.SearchViewModel
import io.legado.app.ui.book.source.manage.BookSourceActivity import io.legado.app.ui.book.source.manage.BookSourceActivity
import io.legado.app.ui.book.cache.CacheRouteScreen import io.legado.app.ui.book.manage.BookshelfManageRouteScreen
import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.config.ConfigNavScreen import io.legado.app.ui.config.ConfigNavScreen
import io.legado.app.ui.config.ConfigTag import io.legado.app.ui.config.ConfigTag
@@ -161,7 +161,7 @@ open class MainActivity : BaseComposeActivity() {
} }
} }
fun createCacheIntent( fun createBookshelfManageScreenIntent(
context: Context, context: Context,
groupId: Long = -1L groupId: Long = -1L
): Intent { ): Intent {
@@ -171,6 +171,11 @@ open class MainActivity : BaseComposeActivity() {
} }
} }
fun createCacheIntent(
context: Context,
groupId: Long = -1L
): Intent = createBookshelfManageScreenIntent(context, groupId)
fun createSearchIntent( fun createSearchIntent(
context: Context, context: Context,
key: String? = null, key: String? = null,
@@ -463,7 +468,7 @@ open class MainActivity : BaseComposeActivity() {
} }
entry<MainRouteCache> { route -> entry<MainRouteCache> { route ->
CacheRouteScreen( BookshelfManageRouteScreen(
groupId = route.groupId, groupId = route.groupId,
onBackClick = { navigateBack(backStack) } onBackClick = { navigateBack(backStack) }
) )
@@ -35,6 +35,7 @@ import io.legado.app.constant.BookType
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.card.GlassCard
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.cover.CoilBookCover import io.legado.app.ui.widget.components.cover.CoilBookCover
import io.legado.app.ui.widget.components.cover.BookshelfCover import io.legado.app.ui.widget.components.cover.BookshelfCover
@@ -55,6 +56,7 @@ fun BookshelfItem(
title: String, title: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isSelected: Boolean = false, isSelected: Boolean = false,
titleEnd: @Composable (() -> Unit)? = null,
subTitle: String? = null, subTitle: String? = null,
desc: String? = null, desc: String? = null,
extra: @Composable (RowScope.() -> Unit)? = null, extra: @Composable (RowScope.() -> Unit)? = null,
@@ -63,12 +65,12 @@ fun BookshelfItem(
titleMaxLines: Int = 2, titleMaxLines: Int = 2,
coverShadow: Boolean = false, coverShadow: Boolean = false,
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit onLongClick: (() -> Unit)?
) { ) {
val selectedColor = if (isSelected) { val selectedColor = if (isSelected) {
LegadoTheme.colorScheme.secondaryContainer.copy(alpha = 0.55f) LegadoTheme.colorScheme.secondaryContainer
} else { } else {
Color.Transparent LegadoTheme.colorScheme.surface
} }
if (isGrid) { if (isGrid) {
Box( Box(
@@ -142,16 +144,19 @@ fun BookshelfItem(
} else { } else {
// 列表布局 // 列表布局
Column { Column {
Row( GlassCard(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.clip(MaterialTheme.shapes.small) .padding(all = 4.dp),
.background(selectedColor) cornerRadius = 8.dp,
.combinedClickable( containerColor = selectedColor,
onClick = onClick, onClick = onClick,
onLongClick = onLongClick onLongClick = onLongClick
) ) {
.padding(horizontal = 16.dp, vertical = 8.dp), Row(
modifier = Modifier
.fillMaxWidth()
.padding(all = 4.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
cover( cover(
@@ -162,42 +167,46 @@ fun BookshelfItem(
Column( Column(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.Center verticalArrangement = Arrangement.Center
) {
Row(
verticalAlignment = Alignment.CenterVertically
) { ) {
AppText( AppText(
text = title, text = title,
style = LegadoTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold), style = LegadoTheme.typography.titleMediumEmphasized,
maxLines = if (!isCompact) 2 else 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
) )
titleEnd?.invoke()
}
subTitle?.let { subTitle?.let {
AppText( AppText(
text = it, text = it,
style = LegadoTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(top = 2.dp)
) )
} }
if (!isCompact) { if (!isCompact) {
desc?.let { desc?.let {
AppText( AppText(
text = it, text = it,
style = LegadoTheme.typography.bodySmall, style = LegadoTheme.typography.labelSmallEmphasized,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis
modifier = Modifier.padding(top = 2.dp)
) )
} }
} }
extra?.let { extra?.let {
Row( Row(
modifier = Modifier.padding(top = 2.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
content = it content = it
) )
} }
} }
} }
}
if (BookshelfConfig.bookshelfShowDivider) if (BookshelfConfig.bookshelfShowDivider)
HorizontalDivider( HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
@@ -325,7 +334,7 @@ fun BookGroupItemGrid(
coverShadow: Boolean = false, coverShadow: Boolean = false,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit onLongClick: (() -> Unit)?
) { ) {
BookshelfItem( BookshelfItem(
isGrid = true, isGrid = true,
@@ -362,7 +371,7 @@ fun BookGroupItemList(
coverShadow: Boolean = false, coverShadow: Boolean = false,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit onLongClick: (() -> Unit)?
) { ) {
BookshelfItem( BookshelfItem(
isGrid = false, isGrid = false,
@@ -397,9 +406,10 @@ fun BookItem(
isSearchMode: Boolean = false, isSearchMode: Boolean = false,
searchKey: String = "", searchKey: String = "",
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit onLongClick: (() -> Unit)?
) { ) {
val unreadCount = book.getUnreadChapterNum() val unreadCount = book.getUnreadChapterNum()
val unreadText = if (BookshelfConfig.showUnread && unreadCount > 0) unreadCount.toString() else null
val bookTypeLabel = if (BookshelfConfig.showTip) { val bookTypeLabel = if (BookshelfConfig.showTip) {
when { when {
book.isAudio -> stringResource(R.string.audio) book.isAudio -> stringResource(R.string.audio)
@@ -427,6 +437,16 @@ fun BookItem(
isCompact = isCompact, isCompact = isCompact,
isSelected = isSelected, isSelected = isSelected,
modifier = modifier, modifier = modifier,
titleEnd = if (layoutMode == 0 && unreadText != null) {
{
TextCard(
text = unreadText,
cornerRadius = 4.dp,
horizontalPadding = 4.dp,
verticalPadding = 0.dp
)
}
} else null,
cover = { modifier -> cover = { modifier ->
BookshelfCover( BookshelfCover(
name = book.name, name = book.name,
@@ -434,7 +454,7 @@ fun BookItem(
path = book.getDisplayCover(), path = book.getDisplayCover(),
isUpdating = isUpdating, isUpdating = isUpdating,
modifier = modifier, modifier = modifier,
badgeText = if (BookshelfConfig.showUnread && unreadCount > 0) unreadCount.toString() else null, badgeText = if (layoutMode != 0) unreadText else null,
showBadgeDot = BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && book.isNew, showBadgeDot = BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && book.isNew,
leftBottomText = matchedSourceLabel ?: bookTypeLabel leftBottomText = matchedSourceLabel ?: bookTypeLabel
) )
@@ -445,19 +465,19 @@ fun BookItem(
} else { } else {
book.author book.author
}, },
desc = stringResource(R.string.read_dur_progress, book.durChapterTitle ?: ""), desc = book.durChapterTitle ?: "",
extra = { extra = {
if (BookshelfConfig.showLastUpdateTime && !book.isLocal) { if (BookshelfConfig.showLastUpdateTime && !book.isLocal) {
AppText( AppText(
text = book.latestChapterTime.toTimeAgo(), text = book.latestChapterTime.toTimeAgo(),
style = LegadoTheme.typography.bodySmall, style = LegadoTheme.typography.labelSmallEmphasized,
color = if (layoutMode != 0 || !isCompact) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary, color = if (layoutMode != 0 || !isCompact) MaterialTheme.colorScheme.outline else MaterialTheme.colorScheme.outline,
modifier = Modifier.padding(end = 4.dp) modifier = Modifier.padding(end = 4.dp)
) )
} }
AppText( AppText(
text = book.latestChapterTitle ?: "", text = book.latestChapterTitle ?: "",
style = LegadoTheme.typography.bodySmall, style = LegadoTheme.typography.labelSmallEmphasized.copy(color = LegadoTheme.colorScheme.outline),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
@@ -191,6 +191,13 @@ fun BookshelfConfigSheet(
onCheckedChange = { BookshelfConfig.bookshelfCoverShadow = it } onCheckedChange = { BookshelfConfig.bookshelfCoverShadow = it }
) )
CompactSwitchSettingItem(
title = "搜索按钮优先打开筛选栏",
checked = BookshelfConfig.bookshelfSearchActionDirectToSearch,
color = MaterialTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.bookshelfSearchActionDirectToSearch = it }
)
// Switches // Switches
CompactSwitchSettingItem( CompactSwitchSettingItem(
title = stringResource(R.string.show_unread), title = stringResource(R.string.show_unread),
@@ -23,12 +23,14 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.FormatListBulleted import androidx.compose.material.icons.automirrored.filled.FormatListBulleted
import androidx.compose.material.icons.filled.Bookmarks import androidx.compose.material.icons.filled.Bookmarks
import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.CloudDownload
import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.GridView import androidx.compose.material.icons.filled.GridView
@@ -38,6 +40,7 @@ import androidx.compose.material.icons.filled.Link
import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Save import androidx.compose.material.icons.filled.Save
import androidx.compose.material.icons.filled.SelectAll import androidx.compose.material.icons.filled.SelectAll
import androidx.compose.material.icons.filled.UploadFile
import androidx.compose.material.icons.filled.Wifi import androidx.compose.material.icons.filled.Wifi
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@@ -61,10 +64,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
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
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
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 androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
@@ -77,6 +82,7 @@ import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.ThemeResolver import io.legado.app.ui.theme.ThemeResolver
import io.legado.app.ui.theme.adaptiveContentPadding import io.legado.app.ui.theme.adaptiveContentPadding
import io.legado.app.ui.theme.adaptiveContentPaddingBookshelf
import io.legado.app.ui.theme.adaptiveHorizontalPadding import io.legado.app.ui.theme.adaptiveHorizontalPadding
import io.legado.app.ui.theme.adaptiveHorizontalPaddingTab import io.legado.app.ui.theme.adaptiveHorizontalPaddingTab
import io.legado.app.ui.widget.components.EmptyMessage import io.legado.app.ui.widget.components.EmptyMessage
@@ -96,12 +102,15 @@ import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.tabRow.AppTabRow import io.legado.app.ui.widget.components.tabRow.AppTabRow
import io.legado.app.ui.widget.components.text.AppText import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.move
import io.legado.app.utils.readText import io.legado.app.utils.readText
import io.legado.app.utils.toastOnUi import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
import sh.calvin.reorderable.ReorderableItem
import sh.calvin.reorderable.rememberReorderableLazyGridState
@OptIn( @OptIn(
ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class,
@@ -315,10 +324,6 @@ fun BookshelfScreen(
if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscape else BookshelfConfig.bookshelfLayoutGridPortrait if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscape else BookshelfConfig.bookshelfLayoutGridPortrait
val bookshelfLayoutList = val bookshelfLayoutList =
if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait
val totalHorizontalPadding =
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) 12.dp else 16.dp
val gridContentHorizontalPadding = totalHorizontalPadding / 2
val gridInnerHorizontalPadding = totalHorizontalPadding / 2
val currentMenuGroupId = if (uiState.isSearch) uiState.selectedGroupId else currentTabGroupId val currentMenuGroupId = if (uiState.isSearch) uiState.selectedGroupId else currentTabGroupId
val editStickySummary = if (isEditMode) { val editStickySummary = if (isEditMode) {
BookshelfEditStickySummary( BookshelfEditStickySummary(
@@ -337,10 +342,14 @@ fun BookshelfScreen(
state = uiState, state = uiState,
showSearchAction = true, showSearchAction = true,
onSearchToggle = { active -> onSearchToggle = { active ->
if (BookshelfConfig.bookshelfSearchActionDirectToSearch) {
onNavigateToSearch(uiState.searchKey.trim())
} else {
viewModel.setSearchMode(active) viewModel.setSearchMode(active)
if (!active && uiState.selectedGroupId != currentTabGroupId) { if (!active && uiState.selectedGroupId != currentTabGroupId) {
viewModel.changeGroup(currentTabGroupId) viewModel.changeGroup(currentTabGroupId)
} }
}
}, },
onSearchQueryChange = { viewModel.setSearchKey(it) }, onSearchQueryChange = { viewModel.setSearchKey(it) },
onSearchSubmit = { rawQuery -> onSearchSubmit = { rawQuery ->
@@ -441,14 +450,14 @@ fun BookshelfScreen(
leadingIcon = { Icon(Icons.Default.Edit, null) } leadingIcon = { Icon(Icons.Default.Edit, null) }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = stringResource(R.string.cache_export), text = stringResource(R.string.bookshelf_management),
onClick = { onClick = {
val groupId = val groupId =
uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
onNavigateToCache(groupId) onNavigateToCache(groupId)
dismiss() dismiss()
}, },
leadingIcon = { Icon(Icons.Default.Download, null) } leadingIcon = { Icon(Icons.Default.Bookmarks, null) }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = stringResource(R.string.export_bookshelf), text = stringResource(R.string.export_bookshelf),
@@ -456,11 +465,12 @@ fun BookshelfScreen(
showExportSheet = true showExportSheet = true
dismiss() dismiss()
}, },
leadingIcon = { Icon(Icons.Default.ImportExport, null) } leadingIcon = { Icon(Icons.Default.UploadFile, null) }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = stringResource(R.string.import_bookshelf), text = stringResource(R.string.import_bookshelf),
onClick = { showImportSheet = true; dismiss() } onClick = { showImportSheet = true; dismiss() },
leadingIcon = { Icon(Icons.Default.CloudDownload, null) }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = stringResource(R.string.log), text = stringResource(R.string.log),
@@ -633,12 +643,11 @@ fun BookshelfScreen(
FastScrollLazyVerticalGrid( FastScrollLazyVerticalGrid(
columns = GridCells.Fixed(folderColumns.coerceAtLeast(1)), columns = GridCells.Fixed(folderColumns.coerceAtLeast(1)),
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize(),
.padding(horizontal = if (isGridMode) gridInnerHorizontalPadding else 0.dp), contentPadding = adaptiveContentPaddingBookshelf(
contentPadding = adaptiveContentPadding(
top = paddingValues.calculateTopPadding(), top = paddingValues.calculateTopPadding(),
bottom = 120.dp, bottom = 120.dp,
horizontal = if (isGridMode) gridContentHorizontalPadding else 0.dp horizontal = if (isGridMode) 8.dp else 4.dp
), ),
verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp), verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp), horizontalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
@@ -701,7 +710,9 @@ fun BookshelfScreen(
bookshelfLayoutList = bookshelfLayoutList, bookshelfLayoutList = bookshelfLayoutList,
isEditMode = isEditMode, isEditMode = isEditMode,
selectedBookUrls = selectedBookUrls, selectedBookUrls = selectedBookUrls,
canReorderBooks = false,
onToggleBookSelection = { toggleBookSelection(it.bookUrl) }, onToggleBookSelection = { toggleBookSelection(it.bookUrl) },
onSaveBookOrder = {},
onGlobalSearch = { onNavigateToSearch(uiState.searchKey.trim()) }, onGlobalSearch = { onNavigateToSearch(uiState.searchKey.trim()) },
onBookClick = onBookClick, onBookClick = onBookClick,
onBookLongClick = onBookLongClick onBookLongClick = onBookLongClick
@@ -728,7 +739,13 @@ fun BookshelfScreen(
bookshelfLayoutList = bookshelfLayoutList, bookshelfLayoutList = bookshelfLayoutList,
isEditMode = isEditMode, isEditMode = isEditMode,
selectedBookUrls = selectedBookUrls, selectedBookUrls = selectedBookUrls,
canReorderBooks = isEditMode &&
!uiState.isSearch &&
group.getRealBookSort() == 3,
onToggleBookSelection = { toggleBookSelection(it.bookUrl) }, onToggleBookSelection = { toggleBookSelection(it.bookUrl) },
onSaveBookOrder = { reorderedBooks ->
viewModel.saveBookOrder(reorderedBooks)
},
onGlobalSearch = { onNavigateToSearch(uiState.searchKey.trim()) }, onGlobalSearch = { onNavigateToSearch(uiState.searchKey.trim()) },
onBookClick = onBookClick, onBookClick = onBookClick,
onBookLongClick = onBookLongClick onBookLongClick = onBookLongClick
@@ -960,7 +977,9 @@ fun BookshelfPage(
bookshelfLayoutList: Int, bookshelfLayoutList: Int,
isEditMode: Boolean, isEditMode: Boolean,
selectedBookUrls: Set<String>, selectedBookUrls: Set<String>,
canReorderBooks: Boolean,
onToggleBookSelection: (BookShelfItem) -> Unit, onToggleBookSelection: (BookShelfItem) -> Unit,
onSaveBookOrder: (books: List<BookShelfItem>) -> Unit,
onGlobalSearch: () -> Unit, onGlobalSearch: () -> Unit,
onBookClick: (BookShelfItem) -> Unit, onBookClick: (BookShelfItem) -> Unit,
onBookLongClick: (BookShelfItem) -> Unit onBookLongClick: (BookShelfItem) -> Unit
@@ -998,25 +1017,81 @@ fun BookshelfPage(
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) 12.dp else 16.dp if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) 12.dp else 16.dp
val gridContentHorizontalPadding = totalHorizontalPadding / 2 val gridContentHorizontalPadding = totalHorizontalPadding / 2
val gridInnerHorizontalPadding = totalHorizontalPadding / 2 val gridInnerHorizontalPadding = totalHorizontalPadding / 2
val hapticFeedback = LocalHapticFeedback.current
var draggingBooks by remember { mutableStateOf<List<BookShelfItem>?>(null) }
var pendingSavedBooks by remember { mutableStateOf<List<BookShelfItem>?>(null) }
val displayBooks = draggingBooks ?: pendingSavedBooks ?: books
LaunchedEffect(books, pendingSavedBooks, canReorderBooks) {
if (!canReorderBooks) {
draggingBooks = null
pendingSavedBooks = null
return@LaunchedEffect
}
val pending = pendingSavedBooks ?: return@LaunchedEffect
if (books.map { it.bookUrl } == pending.map { it.bookUrl }) {
pendingSavedBooks = null
}
}
val gridState = rememberLazyGridState()
val reorderableState = rememberReorderableLazyGridState(gridState) { from, to ->
if (canReorderBooks) {
draggingBooks = displayBooks.toMutableList().apply {
move(from.index, to.index)
}
hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick)
}
}
LaunchedEffect(reorderableState.isAnyItemDragging) {
if (!reorderableState.isAnyItemDragging) {
draggingBooks?.let { reorderedBooks ->
pendingSavedBooks = reorderedBooks
onSaveBookOrder(reorderedBooks)
draggingBooks = null
}
}
}
FastScrollLazyVerticalGrid( FastScrollLazyVerticalGrid(
columns = GridCells.Fixed(columns.coerceAtLeast(1)), columns = GridCells.Fixed(columns.coerceAtLeast(1)),
state = gridState,
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize(),
.padding(horizontal = if (isGridMode) gridInnerHorizontalPadding else 0.dp), contentPadding = adaptiveContentPaddingBookshelf(
contentPadding = adaptiveContentPadding(
top = paddingValues.calculateTopPadding(), top = paddingValues.calculateTopPadding(),
bottom = 120.dp, bottom = 120.dp,
horizontal = if (isGridMode) gridContentHorizontalPadding else 0.dp horizontal = if (isGridMode) 8.dp else 4.dp
), ),
verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp), verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp), horizontalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
showFastScroll = BookshelfConfig.showBookshelfFastScroller showFastScroll = BookshelfConfig.showBookshelfFastScroller
) { ) {
items(books, key = { it.bookUrl }) { book -> items(displayBooks, key = { it.bookUrl }) { book ->
val isSelected = selectedBookUrls.contains(book.bookUrl) val isSelected = selectedBookUrls.contains(book.bookUrl)
ReorderableItem(
state = reorderableState,
key = book.bookUrl,
enabled = canReorderBooks
) {
BookItem( BookItem(
book = book, book = book,
modifier = Modifier.animateItem(), modifier = Modifier.then(
if (canReorderBooks) {
Modifier.longPressDraggableHandle(
onDragStarted = {
draggingBooks = displayBooks
hapticFeedback.performHapticFeedback(
HapticFeedbackType.GestureThresholdActivate
)
},
onDragStopped = {
hapticFeedback.performHapticFeedback(
HapticFeedbackType.GestureEnd
)
}
)
} else {
Modifier
}
),
layoutMode = bookshelfLayoutMode, layoutMode = bookshelfLayoutMode,
isSelected = isSelected, isSelected = isSelected,
gridStyle = BookshelfConfig.bookshelfGridLayout, gridStyle = BookshelfConfig.bookshelfGridLayout,
@@ -1035,14 +1110,19 @@ fun BookshelfPage(
onBookClick(book) onBookClick(book)
} }
}, },
onLongClick = { onLongClick = if (canReorderBooks) {
null
} else {
{
if (isEditMode) { if (isEditMode) {
onToggleBookSelection(book) onToggleBookSelection(book)
} else { } else {
onBookLongClick(book) onBookLongClick(book)
} }
} }
}
) )
} }
} }
}
} }
@@ -392,6 +392,24 @@ class BookshelfViewModel(
} }
} }
fun saveBookOrder(reorderedBooks: List<BookShelfItem>) {
if (reorderedBooks.isEmpty()) return
val isDescending = BookshelfConfig.bookshelfSortOrder == 1
val maxOrder = reorderedBooks.size
execute {
val updates = reorderedBooks.mapIndexedNotNull { index, book ->
appDb.bookDao.getBook(book.bookUrl)?.apply {
order = if (isDescending) maxOrder - index else index + 1
}
}
if (updates.isNotEmpty()) {
appDb.bookDao.update(*updates.toTypedArray())
}
}.onError {
context.toastOnUi("排序保存失败\n${it.localizedMessage}")
}
}
fun downloadBooks(bookUrls: Set<String>, downloadAllChapters: Boolean = false) { fun downloadBooks(bookUrls: Set<String>, downloadAllChapters: Boolean = false) {
if (bookUrls.isEmpty()) return if (bookUrls.isEmpty()) return
execute { execute {
@@ -46,6 +46,8 @@ fun RssSortRouteScreen(
var articleStyle by remember(sourceUrl) { mutableIntStateOf(0) } var articleStyle by remember(sourceUrl) { mutableIntStateOf(0) }
var redirectPolicy by remember(sourceUrl) { mutableStateOf(RedirectPolicy.ALLOW_ALL) } var redirectPolicy by remember(sourceUrl) { mutableStateOf(RedirectPolicy.ALLOW_ALL) }
var screenTitle by remember(sourceUrl) { mutableStateOf("") } var screenTitle by remember(sourceUrl) { mutableStateOf("") }
val setSourceVariableText = stringResource(R.string.set_source_variable)
val errorText = stringResource(R.string.error)
var showReadRecordSheet by remember { mutableStateOf(false) } var showReadRecordSheet by remember { mutableStateOf(false) }
var readRecords by remember { mutableStateOf<List<RssReadRecord>>(emptyList()) } var readRecords by remember { mutableStateOf<List<RssReadRecord>>(emptyList()) }
@@ -104,7 +106,7 @@ fun RssSortRouteScreen(
val variable = withContext(Dispatchers.IO) { source.getVariable() } val variable = withContext(Dispatchers.IO) { source.getVariable() }
activity?.showDialogFragment( activity?.showDialogFragment(
VariableDialog( VariableDialog(
context.getString(R.string.set_source_variable), setSourceVariableText,
source.getKey(), source.getKey(),
variable, variable,
comment comment
@@ -141,7 +143,7 @@ fun RssSortRouteScreen(
viewModel.rssSource?.sourceUrl ?: sourceUrl.orEmpty() viewModel.rssSource?.sourceUrl ?: sourceUrl.orEmpty()
} }
if (openOrigin.isBlank()) { if (openOrigin.isBlank()) {
context.toastOnUi(context.getString(R.string.error)) context.toastOnUi(errorText)
} else { } else {
onOpenRead(record.title, openOrigin, null, record.record) onOpenRead(record.title, openOrigin, null, record.record)
} }
@@ -78,3 +78,19 @@ fun adaptiveContentPadding(
end = horizontal end = horizontal
) )
} }
@Composable
fun adaptiveContentPaddingBookshelf(
top: Dp,
bottom: Dp,
horizontal: Dp
): PaddingValues {
val adjustedTop = if (ThemeResolver.isMiuixEngine(composeEngine)) top + 12.dp else top + 8.dp
val horizontal = if (ThemeResolver.isMiuixEngine(composeEngine)) 12.dp + horizontal else 4.dp + horizontal
return PaddingValues(
top = adjustedTop,
bottom = bottom,
start = horizontal,
end = horizontal
)
}
@@ -136,27 +136,6 @@ fun TopBarNavigationButton(
} }
} }
@Deprecated(
message = "Use TopBarNavigationButton for consistent TopBar naming.",
replaceWith = ReplaceWith("TopBarNavigationButton(onClick, modifier, imageVector, contentDescription, style)")
)
@Composable
fun TopbarNavigationButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
imageVector: ImageVector = AppIcons.Back,
contentDescription: String? = stringResource(id = R.string.back),
style: TopBarButtonVariant = TopBarButtonVariant.Filled
) {
TopBarNavigationButton(
onClick = onClick,
modifier = modifier,
imageVector = imageVector,
contentDescription = contentDescription,
style = style
)
}
@Composable @Composable
fun TopBarActionButton( fun TopBarActionButton(
onClick: () -> Unit, onClick: () -> Unit,
@@ -279,7 +279,7 @@ fun VerticalGridFastScroller(
contentPadding: PaddingValues, contentPadding: PaddingValues,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
thumbAllowed: () -> Boolean = { true }, thumbAllowed: () -> Boolean = { true },
thumbColor: Color = MaterialTheme.colorScheme.primary, thumbColor: Color = LegadoTheme.colorScheme.primary,
topContentPadding: Dp = Dp.Hairline, topContentPadding: Dp = Dp.Hairline,
bottomContentPadding: Dp = Dp.Hairline, bottomContentPadding: Dp = Dp.Hairline,
endContentPadding: Dp = Dp.Hairline, endContentPadding: Dp = Dp.Hairline,