diff --git a/app/src/main/java/io/legado/app/domain/usecase/SearchBooksUseCase.kt b/app/src/main/java/io/legado/app/domain/usecase/SearchBooksUseCase.kt index 8f50a5ac1..60a10286b 100644 --- a/app/src/main/java/io/legado/app/domain/usecase/SearchBooksUseCase.kt +++ b/app/src/main/java/io/legado/app/domain/usecase/SearchBooksUseCase.kt @@ -32,6 +32,7 @@ data class BookSearchRequest( val scope: BookSearchScope, val precision: Boolean, val concurrency: Int, + val types: Set? = null, ) sealed interface SearchRunEvent { @@ -88,7 +89,10 @@ class SearchBooksUseCase( sourceParts.map { part -> async(Dispatchers.IO) { val source = gateway.getBookSource(part.bookSourceUrl) ?: return@async null - if (source.bookSourceType != 0 || source.searchUrl.isNullOrBlank()) { + if (source.searchUrl.isNullOrBlank()) { + return@async null + } + if (request.types != null && !request.types.contains(source.bookSourceType)) { return@async null } SearchableSource(part, source) diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchContract.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchContract.kt index 79e992932..36efba3eb 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchContract.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchContract.kt @@ -33,7 +33,9 @@ data class SearchUiState( val hasMore: Boolean = true, val processedSources: Int = 0, val totalSources: Int = 0, + val selectedSourceTypes: Set = emptySet(), val showScopeSheet: Boolean = false, + val showTypeSheet: Boolean = false, val showClearHistoryDialog: Boolean = false, val showSuggestions: Boolean = true, val emptyScopeAction: SearchEmptyScopeAction? = null, @@ -62,6 +64,8 @@ sealed interface SearchIntent { data class SetClearHistoryDialogVisible(val visible: Boolean) : SearchIntent data object ConfirmClearHistory : SearchIntent data class SetScopeSheetVisible(val visible: Boolean) : SearchIntent + data class SetTypeSheetVisible(val visible: Boolean) : SearchIntent + data class ToggleSourceType(val type: Int) : SearchIntent data object SelectAllScope : SearchIntent data class ToggleScopeGroup(val groupName: String) : SearchIntent data class ToggleScopeSource(val source: BookSourcePart) : SearchIntent diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt index c2f024e6f..f0a22618b 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Book import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.History +import androidx.compose.material.icons.filled.Layers import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.Stop import androidx.compose.material3.CircularProgressIndicator @@ -268,6 +269,16 @@ fun SearchScreen( activeText = stringResource(R.string.precision_search), inactiveText = stringResource(R.string.search), ) + TopBarAnimatedActionButton( + checked = state.selectedSourceTypes.isNotEmpty(), + onCheckedChange = { + viewModel.onIntent(SearchIntent.SetTypeSheetVisible(true)) + }, + iconChecked = Icons.Default.Layers, + iconUnchecked = Icons.Default.Layers, + activeText = "搜素类型", + inactiveText = "搜素类型", + ) TopBarAnimatedActionButton( checked = !state.isAllScope, onCheckedChange = { @@ -572,6 +583,49 @@ fun SearchScreen( Spacer(modifier = Modifier.height(20.dp)) } } + + AppModalBottomSheet( + show = state.showTypeSheet, + onDismissRequest = { viewModel.onIntent(SearchIntent.SetTypeSheetVisible(false)) }, + title = "搜素类型", + ) { + Column { + SelectionItemCard( + title = stringResource(R.string.all), + isSelected = state.selectedSourceTypes.isEmpty(), + containerColor = LegadoTheme.colorScheme.onSheetContent, + inSelectionMode = true, + onToggleSelection = { + if (state.selectedSourceTypes.isNotEmpty()) { + state.selectedSourceTypes.forEach { + viewModel.onIntent(SearchIntent.ToggleSourceType(it)) + } + } + } + ) + + Spacer(modifier = Modifier.height(8.dp)) + + listOf( + 0 to stringResource(R.string.noval), + 2 to stringResource(R.string.manga), + 1 to stringResource(R.string.audio), + ).forEach { (type, label) -> + SelectionItemCard( + title = label, + isSelected = state.selectedSourceTypes.contains(type), + containerColor = LegadoTheme.colorScheme.onSheetContent, + inSelectionMode = true, + onToggleSelection = { + viewModel.onIntent(SearchIntent.ToggleSourceType(type)) + } + ) + Spacer(modifier = Modifier.height(4.dp)) + } + + Spacer(modifier = Modifier.height(20.dp)) + } + } } private data class SearchFloatingSummary( diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt index 9b62dfb2e..8213e876f 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt @@ -140,6 +140,23 @@ class SearchViewModel( _uiState.update { it.copy(showScopeSheet = intent.visible) } } + is SearchIntent.SetTypeSheetVisible -> { + _uiState.update { it.copy(showTypeSheet = intent.visible) } + } + + is SearchIntent.ToggleSourceType -> { + _uiState.update { state -> + val current = state.selectedSourceTypes + val next = if (current.contains(intent.type)) { + current - intent.type + } else { + current + intent.type + } + state.copy(selectedSourceTypes = next) + } + restartCommittedSearchIfNeeded() + } + SearchIntent.SelectAllScope -> { val oldScope = searchScope.toString() searchScope.update("") @@ -334,6 +351,7 @@ class SearchViewModel( scope = BookSearchScope(searchScope.toString()), precision = _uiState.value.isPrecisionSearch, concurrency = OtherConfig.threadCount, + types = _uiState.value.selectedSourceTypes.takeIf { it.isNotEmpty() }, ), searchControl )