[优化] 优化书架界面,增加开关以控制搜索按钮优先打开搜索页还是筛选栏
This commit is contained in:
@@ -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
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
+88
-57
@@ -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> {
|
||||||
+177
-127
@@ -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
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动时自动刷新书架
|
* 启动时自动刷新书架
|
||||||
*/
|
*/
|
||||||
|
|||||||
+6
-5
@@ -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,
|
||||||
|
|||||||
+1
-1
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user