diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt index c6a8d6634..2e682e9d8 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt @@ -34,7 +34,6 @@ import androidx.compose.material.icons.outlined.Image import androidx.compose.material.icons.outlined.Settings import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -71,6 +70,7 @@ import io.legado.app.ui.book.changesource.ChangeBookSourceComposeViewModel import io.legado.app.ui.book.changesource.ChangeSourceConfig import io.legado.app.ui.book.changesource.ChangeSourceMigrationOptionsSheet import io.legado.app.ui.book.group.GroupEditSheet +import io.legado.app.ui.book.search.ScopeSelectSheet import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.book.source.manage.BookSourceActivity import io.legado.app.ui.theme.LegadoTheme @@ -298,7 +298,6 @@ fun ChangeSourceSheet( var loadingAction by remember { mutableStateOf(false) } var showOptionsMenu by rememberSaveable { mutableStateOf(false) } var showFilterSheet by rememberSaveable { mutableStateOf(false) } - var scopeSheetTab by rememberSaveable { mutableIntStateOf(0) } val bookAddedToShelfText = stringResource(R.string.book_added_to_shelf) val editSourceResult = rememberLauncherForActivityResult(StartActivityContract(BookSourceEditActivity::class.java)) { @@ -603,93 +602,22 @@ fun ChangeSourceSheet( } ) - AppModalBottomSheet( + ScopeSelectSheet( show = showFilterSheet, onDismissRequest = { showFilterSheet = false }, - title = stringResource(R.string.search_select_group), - endAction = { - MediumIconButton( - onClick = { - viewModel.startSearch() - showFilterSheet = false - }, - imageVector = Icons.Default.Check - ) + isAll = scopeState.isAll, + onSelectAll = { viewModel.selectAllScope() }, + groups = groups, + selectedGroups = scopeState.displayNames, + onToggleGroup = { viewModel.toggleScopeGroup(it) }, + sources = enabledSources, + selectedSources = scopeState.sourceUrls, + onToggleSource = { viewModel.toggleScopeSource(it) }, + isSourceScope = scopeState.isSource, + onConfirm = { + viewModel.startSearch() + showFilterSheet = false } - ) { - Column { - SelectionItemCard( - title = stringResource(R.string.all_source), - isSelected = scopeState.isAll, - containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f), - inSelectionMode = true, - onToggleSelection = { - viewModel.selectAllScope() - } - ) - - Spacer(modifier = Modifier.height(8.dp)) - AppTabRow( - tabTitles = listOf( - stringResource(R.string.group), - stringResource(R.string.book_source), - ), - selectedTabIndex = scopeSheetTab, - onTabSelected = { scopeSheetTab = it }, - ) - Spacer(modifier = Modifier.height(6.dp)) - - if (scopeSheetTab == 0) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - items(groups, key = { it }) { - val selected = !scopeState.isSource && scopeState.displayNames.contains(it) - SelectionItemCard( - title = it, - isSelected = selected, - containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f), - inSelectionMode = true, - onToggleSelection = { - viewModel.toggleScopeGroup(it) - } - ) - } - } - } else { - if (enabledSources.isEmpty()) { - Text( - text = stringResource(R.string.search_empty), - style = LegadoTheme.typography.bodyMedium, - color = LegadoTheme.colorScheme.onSurfaceVariant, - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp), - ) - } else { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - items(enabledSources, key = { it.bookSourceUrl }) { - val selected = scopeState.sourceUrls.contains(it.bookSourceUrl) - SelectionItemCard( - title = it.bookSourceName, - subtitle = it.bookSourceGroup?.takeIf { group -> group.isNotBlank() }, - containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f), - isSelected = selected, - inSelectionMode = true, - onToggleSelection = { - viewModel.toggleScopeSource(it) - } - ) - } - } - } - } - - Spacer(modifier = Modifier.height(20.dp)) - } - } + ) } diff --git a/app/src/main/java/io/legado/app/ui/book/search/ScopeSelectSheet.kt b/app/src/main/java/io/legado/app/ui/book/search/ScopeSelectSheet.kt new file mode 100644 index 000000000..7c925ca4e --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/search/ScopeSelectSheet.kt @@ -0,0 +1,165 @@ +package io.legado.app.ui.book.search + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import io.legado.app.R +import io.legado.app.data.entities.BookSourcePart +import io.legado.app.ui.theme.LegadoTheme +import io.legado.app.ui.widget.components.SearchBar +import io.legado.app.ui.widget.components.button.MediumIconButton +import io.legado.app.ui.widget.components.card.SelectionItemCard +import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet +import io.legado.app.ui.widget.components.tabRow.AppTabRow + +@Composable +fun ScopeSelectSheet( + show: Boolean, + onDismissRequest: () -> Unit, + isAll: Boolean, + onSelectAll: () -> Unit, + groups: List, + selectedGroups: Collection, + onToggleGroup: (String) -> Unit, + sources: List, + selectedSources: Collection, + onToggleSource: (BookSourcePart) -> Unit, + isSourceScope: Boolean = false, + title: String = stringResource(R.string.search_select_group), + onConfirm: (() -> Unit)? = null, +) { + var scopeSheetTab by rememberSaveable(show) { mutableIntStateOf(if (isSourceScope) 1 else 0) } + var filterText by rememberSaveable(show) { mutableStateOf("") } + + val filteredGroups = remember(groups, filterText) { + if (filterText.isBlank()) groups else groups.filter { it.contains(filterText, ignoreCase = true) } + } + val filteredSources = remember(sources, filterText) { + if (filterText.isBlank()) sources else sources.filter { + it.bookSourceName.contains(filterText, ignoreCase = true) || + it.bookSourceGroup?.contains(filterText, ignoreCase = true) == true || + it.bookSourceUrl.contains(filterText, ignoreCase = true) + } + } + + AppModalBottomSheet( + show = show, + onDismissRequest = onDismissRequest, + title = title, + endAction = onConfirm?.let { + { + MediumIconButton( + onClick = it, + imageVector = Icons.Default.Check + ) + } + } + ) { + Column { + + SearchBar( + query = filterText, + onQueryChange = { filterText = it }, + placeholder = stringResource(R.string.screen), + autoFocus = false + ) + + Spacer(modifier = Modifier.height(8.dp)) + + SelectionItemCard( + title = stringResource(R.string.all_source), + isSelected = isAll, + containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f), + inSelectionMode = true, + onToggleSelection = { + onSelectAll() + } + ) + + Spacer(modifier = Modifier.height(8.dp)) + + AppTabRow( + tabTitles = listOf( + stringResource(R.string.group), + stringResource(R.string.book_source), + ), + selectedTabIndex = scopeSheetTab, + onTabSelected = { scopeSheetTab = it }, + ) + Spacer(modifier = Modifier.height(6.dp)) + + if (scopeSheetTab == 0) { + LazyColumn( + modifier = Modifier.heightIn(max = 480.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + items(filteredGroups, key = { it }) { groupName -> + val selected = !isSourceScope && selectedGroups.contains(groupName) + SelectionItemCard( + title = groupName, + isSelected = selected, + containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f), + inSelectionMode = true, + onToggleSelection = { + onToggleGroup(groupName) + } + ) + } + } + } else { + if (filteredSources.isEmpty()) { + Text( + text = stringResource(R.string.search_empty), + style = LegadoTheme.typography.bodyMedium, + color = LegadoTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp), + ) + } else { + LazyColumn( + modifier = Modifier.heightIn(max = 480.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + items(filteredSources, key = { it.bookSourceUrl }) { source -> + val selected = selectedSources.contains(source.bookSourceUrl) + SelectionItemCard( + title = source.bookSourceName, + subtitle = source.bookSourceGroup?.takeIf { group -> group.isNotBlank() }, + containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f), + isSelected = selected, + inSelectionMode = true, + onToggleSelection = { + onToggleSource(source) + } + ) + } + } + } + } + + Spacer(modifier = Modifier.height(20.dp)) + } + } +} 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 602bc5b59..ec619ca14 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 @@ -39,7 +39,6 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState @@ -80,6 +79,7 @@ import io.legado.app.ui.widget.components.topbar.TopBarAnimatedActionButton import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton import io.legado.app.ui.widget.components.icon.AppIcon import io.legado.app.ui.widget.components.icon.AppIcons +import io.legado.app.ui.book.search.ScopeSelectSheet import io.legado.app.ui.widget.components.list.TopFloatingStickyItem import io.legado.app.ui.widget.components.tabRow.AppTabRow import io.legado.app.ui.widget.components.text.AppText @@ -104,7 +104,6 @@ fun SearchScreen( val listState = rememberLazyListState() val lifecycleOwner = LocalLifecycleOwner.current var queryInput by rememberSaveable { mutableStateOf(state.query) } - var scopeSheetTab by rememberSaveable { mutableIntStateOf(0) } var ignoreNextDebouncedQuery by rememberSaveable { mutableStateOf(null) } val showSuggestionPanel = state.showSuggestions val latestQuery by rememberUpdatedState(state.query) @@ -128,12 +127,6 @@ fun SearchScreen( } } - LaunchedEffect(state.showScopeSheet, state.isSourceScope) { - if (state.showScopeSheet) { - scopeSheetTab = if (state.isSourceScope) 1 else 0 - } - } - LaunchedEffect(Unit) { snapshotFlow { queryInput } .distinctUntilChanged() @@ -505,85 +498,19 @@ fun SearchScreen( } ) - AppModalBottomSheet( + ScopeSelectSheet( show = state.showScopeSheet, onDismissRequest = { viewModel.onIntent(SearchIntent.SetScopeSheetVisible(false)) }, - title = stringResource(R.string.search_select_group), - ) { - Column { - SelectionItemCard( - title = stringResource(R.string.all_source), - isSelected = state.isAllScope, - containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f), - inSelectionMode = true, - onToggleSelection = { - viewModel.onIntent(SearchIntent.SelectAllScope) - } - ) - - Spacer(modifier = Modifier.height(8.dp)) - AppTabRow( - tabTitles = listOf( - stringResource(R.string.group), - stringResource(R.string.book_source), - ), - selectedTabIndex = scopeSheetTab, - onTabSelected = { scopeSheetTab = it }, - ) - Spacer(modifier = Modifier.height(6.dp)) - - if (scopeSheetTab == 0) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - items(state.enabledGroups, key = { it }) { - val selected = !state.isSourceScope && state.scopeDisplayNames.contains(it) - SelectionItemCard( - title = it, - isSelected = selected, - containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f), - inSelectionMode = true, - onToggleSelection = { - viewModel.onIntent(SearchIntent.ToggleScopeGroup(it)) - } - ) - } - } - } else { - if (state.enabledSources.isEmpty()) { - Text( - text = stringResource(R.string.search_empty), - style = LegadoTheme.typography.bodyMedium, - color = LegadoTheme.colorScheme.onSurfaceVariant, - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp), - ) - } else { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - items(state.enabledSources, key = { it.bookSourceUrl }) { - val selected = state.selectedScopeSourceUrls.contains(it.bookSourceUrl) - SelectionItemCard( - title = it.bookSourceName, - subtitle = it.bookSourceGroup?.takeIf { group -> group.isNotBlank() }, - containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f), - isSelected = selected, - inSelectionMode = true, - onToggleSelection = { - viewModel.onIntent(SearchIntent.ToggleScopeSource(it)) - } - ) - } - } - } - } - - Spacer(modifier = Modifier.height(20.dp)) - } - } + isAll = state.isAllScope, + onSelectAll = { viewModel.onIntent(SearchIntent.SelectAllScope) }, + groups = state.enabledGroups, + selectedGroups = state.scopeDisplayNames, + onToggleGroup = { viewModel.onIntent(SearchIntent.ToggleScopeGroup(it)) }, + sources = state.enabledSources, + selectedSources = state.selectedScopeSourceUrls, + onToggleSource = { viewModel.onIntent(SearchIntent.ToggleScopeSource(it)) }, + isSourceScope = state.isSourceScope + ) AppModalBottomSheet( show = state.showTypeSheet, diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfUiState.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfUiState.kt index 2f421995e..fde085027 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfUiState.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfUiState.kt @@ -26,6 +26,7 @@ sealed interface BookshelfOverlay { data object LogSheet : BookshelfOverlay data object GroupMenu : BookshelfOverlay data object GroupSelectSheet : BookshelfOverlay + data class GroupEditSheet(val groupId: Long) : BookshelfOverlay data object BatchDownloadConfirmDialog : BookshelfOverlay }