From 828a3d09b6167b68a03518be8b71e03d86c9a69e Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sun, 3 Dec 2023 19:09:08 +0800 Subject: [PATCH] Move advanced options below search bar --- .../ehviewer/client/data/ListUrlBuilder.kt | 10 +- .../hippo/ehviewer/ui/main/AdvanceTable.kt | 6 +- .../hippo/ehviewer/ui/main/AdvancedSearch.kt | 136 ---------------- .../hippo/ehviewer/ui/main/NormalSearch.kt | 8 - .../ehviewer/ui/screen/GalleryListScreen.kt | 54 ++----- .../ehviewer/ui/screen/SearchBarScreen.kt | 151 +++++++++++++++++- .../com/hippo/ehviewer/ui/tools/Dialog.kt | 22 ++- app/src/main/res/values-zh-rCN/strings.xml | 4 +- app/src/main/res/values-zh-rHK/strings.xml | 4 +- app/src/main/res/values-zh-rTW/strings.xml | 4 +- app/src/main/res/values/strings.xml | 4 +- 11 files changed, 193 insertions(+), 210 deletions(-) delete mode 100644 app/src/main/kotlin/com/hippo/ehviewer/ui/main/AdvancedSearch.kt diff --git a/app/src/main/kotlin/com/hippo/ehviewer/client/data/ListUrlBuilder.kt b/app/src/main/kotlin/com/hippo/ehviewer/client/data/ListUrlBuilder.kt index 5885117f5..b967d9a5c 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/client/data/ListUrlBuilder.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/client/data/ListUrlBuilder.kt @@ -271,7 +271,7 @@ data class ListUrlBuilder( addQueryParameterIfNotBlank("next", mNext) addQueryParameterIfNotBlank("seek", mJumpTo) // Advance search - if (advanceSearch != -1) { + if (advanceSearch > 0 || minRating > 0 || pageFrom > 0 || pageTo > 0) { addQueryParameter("advsearch", "1") if (advanceSearch and AdvanceTable.SH != 0) { addQueryParameter("f_sh", "on") @@ -289,20 +289,20 @@ data class ListUrlBuilder( addQueryParameter("f_sft", "on") } // Set min star - if (minRating != -1) { + if (minRating > 0) { addQueryParameter("f_sr", "on") addQueryParameter("f_srdd", "$minRating") } // Pages - if (pageFrom != -1 || pageTo != -1) { + if (pageFrom > 0 || pageTo > 0) { addQueryParameter("f_sp", "on") addQueryParameterIfNotBlank( "f_spf", - pageFrom.takeUnless { it == -1 }?.toString(), + pageFrom.takeIf { it > 0 }?.toString(), ) addQueryParameterIfNotBlank( "f_spt", - pageTo.takeUnless { it == -1 }?.toString(), + pageTo.takeIf { it > 0 }?.toString(), ) } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AdvanceTable.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AdvanceTable.kt index 983d81302..a079a022c 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AdvanceTable.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AdvanceTable.kt @@ -14,7 +14,7 @@ object AdvanceTable { @Parcelize data class AdvancedSearchOption( val advanceSearch: Int = 0, - val minRating: Int = -1, - val fromPage: Int = -1, - val toPage: Int = -1, + val minRating: Int = 0, + val fromPage: Int = 0, + val toPage: Int = 0, ) : Parcelable diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AdvancedSearch.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AdvancedSearch.kt deleted file mode 100644 index f51e38919..000000000 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AdvancedSearch.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.hippo.ehviewer.ui.main - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExposedDropdownMenuBox -import androidx.compose.material3.ExposedDropdownMenuDefaults -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.ProvideTextStyle -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.getValue -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.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.stringArrayResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.unit.dp -import com.hippo.ehviewer.R -import com.hippo.ehviewer.ui.tools.LabeledCheckbox -import com.hippo.ehviewer.util.toIntOrDefault - -@Composable -fun SearchAdvanced( - modifier: Modifier = Modifier, - state: AdvancedSearchOption, - onStateChanged: (AdvancedSearchOption) -> Unit, -) = FlowRow(modifier = modifier, horizontalArrangement = Arrangement.Center, maxItemsInEachRow = 2) { - ProvideTextStyle(MaterialTheme.typography.bodyMedium) { - val adv = state.advanceSearch - fun checked(bit: Int) = adv and bit != 0 - fun AdvancedSearchOption.inv(bit: Int) = onStateChanged(copy(advanceSearch = advanceSearch xor bit)) - LabeledCheckbox( - checked = checked(AdvanceTable.SH), - onCheckedChange = { state.inv(AdvanceTable.SH) }, - label = stringResource(id = R.string.search_sh), - ) - LabeledCheckbox( - checked = checked(AdvanceTable.STO), - onCheckedChange = { state.inv(AdvanceTable.STO) }, - label = stringResource(id = R.string.search_sto), - ) - val minRatingItems = stringArrayResource(id = R.array.search_min_rating) - var expanded by remember { mutableStateOf(false) } - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { expanded = !expanded }, - modifier = Modifier.align(Alignment.CenterVertically).padding(12.dp), - ) { - val softwareKeyboardController = LocalSoftwareKeyboardController.current - OutlinedTextField( - modifier = Modifier.menuAnchor(), - readOnly = true, - value = minRatingItems[if (state.minRating in 2..5) state.minRating - 1 else 0], - onValueChange = {}, - label = { Text(stringResource(id = R.string.search_sr)) }, - trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, - colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(), - ) - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - SideEffect { - softwareKeyboardController?.hide() - } - minRatingItems.forEach { selectionOption -> - DropdownMenuItem( - text = { Text(selectionOption) }, - onClick = { - expanded = false - val index = minRatingItems.indexOf(selectionOption) - onStateChanged(state.copy(minRating = if (index > 0) index + 1 else -1)) - }, - contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, - ) - } - } - } - Row(verticalAlignment = Alignment.CenterVertically) { - var enabled by rememberSaveable { mutableStateOf(false) } - LabeledCheckbox( - checked = enabled, - onCheckedChange = { enabled = it }, - label = stringResource(id = R.string.search_sp), - ) - OutlinedTextField( - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword), - value = if (enabled && state.fromPage != -1) state.fromPage.toString() else "", - onValueChange = { onStateChanged(state.copy(fromPage = it.toIntOrDefault(-1))) }, - modifier = Modifier.width(96.dp).padding(16.dp), - singleLine = true, - enabled = enabled, - ) - Text(text = stringResource(id = R.string.search_sp_to)) - OutlinedTextField( - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword), - value = if (enabled && state.toPage != -1) state.toPage.toString() else "", - onValueChange = { onStateChanged(state.copy(toPage = it.toIntOrDefault(-1))) }, - modifier = Modifier.width(96.dp).padding(16.dp), - singleLine = true, - enabled = enabled, - ) - Text(text = stringResource(id = R.string.search_sp_suffix)) - } - Text(text = stringResource(id = R.string.search_sf), modifier = Modifier.align(Alignment.CenterVertically)) - FlowRow { - LabeledCheckbox( - checked = checked(AdvanceTable.SFL), - onCheckedChange = { state.inv(AdvanceTable.SFL) }, - label = stringResource(id = R.string.search_sfl), - ) - LabeledCheckbox( - checked = checked(AdvanceTable.SFU), - onCheckedChange = { state.inv(AdvanceTable.SFU) }, - label = stringResource(id = R.string.search_sfu), - ) - LabeledCheckbox( - checked = checked(AdvanceTable.SFT), - onCheckedChange = { state.inv(AdvanceTable.SFT) }, - label = stringResource(id = R.string.search_sft), - ) - } - } -} diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/NormalSearch.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/NormalSearch.kt index 8a6ed407a..5f703b317 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/NormalSearch.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/NormalSearch.kt @@ -26,7 +26,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.hippo.ehviewer.R import com.hippo.ehviewer.client.EhUtils -import com.hippo.ehviewer.ui.tools.LabeledCheckbox private val categoryTable = arrayOf( EhUtils.DOUJINSHI to R.string.doujinshi, @@ -55,8 +54,6 @@ fun NormalSearch( onCategoryChanged: (Int) -> Unit, searchMode: Int, onSearchModeChanged: (Int) -> Unit, - isAdvanced: Boolean, - onAdvancedChanged: (Boolean) -> Unit, showInfo: () -> Unit, maxItemsInEachRow: Int, ) { @@ -101,11 +98,6 @@ fun NormalSearch( IconButton(onClick = showInfo) { Icon(imageVector = Icons.AutoMirrored.Default.Help, contentDescription = null) } - LabeledCheckbox( - checked = isAdvanced, - onCheckedChange = onAdvancedChanged, - label = stringResource(id = R.string.search_enable_advance), - ) } } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt index 72f8fc5ea..5abdd8cff 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt @@ -133,7 +133,6 @@ import com.hippo.ehviewer.ui.main.GalleryInfoListItem import com.hippo.ehviewer.ui.main.GalleryList import com.hippo.ehviewer.ui.main.ImageSearch import com.hippo.ehviewer.ui.main.NormalSearch -import com.hippo.ehviewer.ui.main.SearchAdvanced import com.hippo.ehviewer.ui.showDatePicker import com.hippo.ehviewer.ui.tools.Deferred import com.hippo.ehviewer.ui.tools.DragHandle @@ -192,7 +191,6 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) { var showSearchLayout by rememberSaveable { mutableStateOf(false) } var searchNormalMode by rememberSaveable { mutableStateOf(true) } - var searchAdvancedMode by rememberSaveable { mutableStateOf(false) } var category by rememberSaveable { mutableIntStateOf(Settings.searchCategory) } var searchMethod by rememberSaveable { mutableIntStateOf(1) } var advancedSearchOption by rememberSaveable { mutableStateOf(AdvancedSearchOption()) } @@ -534,14 +532,13 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) { var expanded by remember { mutableStateOf(false) } - val searchErr1 = stringResource(R.string.search_sp_err1) - val searchErr2 = stringResource(R.string.search_sp_err2) val selectImageFirst = stringResource(R.string.select_image_first) SearchBarScreen( title = suitableTitle, searchFieldState = searchFieldState, searchFieldHint = searchBarHint, - showLanguageFilter = true, + advancedOption = advancedSearchOption, + onAdvancedOptionChanged = { advancedSearchOption = it }, showSearchFab = showSearchLayout, onApplySearch = { query -> val builder = ListUrlBuilder() @@ -551,6 +548,10 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) { val newMode = if (oldMode == MODE_SUBSCRIPTION) MODE_SUBSCRIPTION else MODE_NORMAL builder.mode = newMode builder.keyword = query + builder.advanceSearch = advancedSearchOption.advanceSearch + builder.minRating = advancedSearchOption.minRating + builder.pageFrom = advancedSearchOption.fromPage + builder.pageTo = advancedSearchOption.toPage } else { if (searchNormalMode) { builder.mode = when (searchMethod) { @@ -561,21 +562,10 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) { } builder.keyword = query builder.category = category - if (searchAdvancedMode) { - builder.advanceSearch = advancedSearchOption.advanceSearch - builder.minRating = advancedSearchOption.minRating - val pageFrom = advancedSearchOption.fromPage - val pageTo = advancedSearchOption.toPage - if (pageTo != -1 && pageTo < 10) { - activity.showTip(searchErr1) - return@SearchBarScreen - } else if (pageFrom != -1 && pageTo != -1 && pageTo - pageFrom < 20) { - activity.showTip(searchErr2) - return@SearchBarScreen - } - builder.pageFrom = pageFrom - builder.pageTo = pageTo - } + builder.advanceSearch = advancedSearchOption.advanceSearch + builder.minRating = advancedSearchOption.minRating + builder.pageFrom = advancedSearchOption.fromPage + builder.pageTo = advancedSearchOption.toPage } else { builder.mode = MODE_IMAGE_SEARCH if (imagePath.isBlank()) { @@ -672,8 +662,6 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) { }, searchMode = searchMethod, onSearchModeChanged = { searchMethod = it }, - isAdvanced = searchAdvancedMode, - onAdvancedChanged = { searchAdvancedMode = it }, showInfo = { BaseDialogBuilder(context).setMessage(R.string.search_tip).show() }, maxItemsInEachRow = when (windowSizeClass.widthSizeClass) { WindowWidthSizeClass.Compact -> 2 @@ -684,28 +672,6 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) { } } } - AnimatedVisibility(visible = searchNormalMode && searchAdvancedMode) { - ElevatedCard(modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.search_layout_margin_v))) { - Column( - modifier = Modifier.padding( - horizontal = dimensionResource(id = R.dimen.search_category_padding_h), - vertical = dimensionResource(id = R.dimen.search_category_padding_v), - ).fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text( - text = stringResource(id = R.string.search_advance), - modifier = Modifier.height(dimensionResource(id = R.dimen.search_category_title_height)), - fontWeight = FontWeight.Bold, - style = MaterialTheme.typography.titleMedium, - ) - SearchAdvanced( - state = advancedSearchOption, - onStateChanged = { advancedSearchOption = it }, - ) - } - } - } AnimatedVisibility(visible = !searchNormalMode) { ElevatedCard(modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.search_layout_margin_v))) { Column( diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SearchBarScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SearchBarScreen.kt index 3feb60422..403de66c7 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SearchBarScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SearchBarScreen.kt @@ -3,7 +3,10 @@ package com.hippo.ehviewer.ui.screen import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.clickable +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -19,8 +22,11 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.union +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text2.input.TextFieldState import androidx.compose.foundation.text2.input.clearText import androidx.compose.foundation.text2.input.forEachTextValue @@ -30,6 +36,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.MenuBook import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.FilterAlt +import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.outlined.FilterAlt @@ -39,6 +46,8 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.SearchBar import androidx.compose.material3.SearchBarDefaults @@ -60,7 +69,9 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalWindowInfo +import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.util.lerp @@ -74,9 +85,12 @@ import com.hippo.ehviewer.dao.Search import com.hippo.ehviewer.dao.SearchDao import com.hippo.ehviewer.ui.LocalNavDrawerState import com.hippo.ehviewer.ui.LockDrawer +import com.hippo.ehviewer.ui.main.AdvanceTable +import com.hippo.ehviewer.ui.main.AdvancedSearchOption import com.hippo.ehviewer.ui.main.FAB_ANIMATE_TIME import com.hippo.ehviewer.ui.tools.IconFix import com.hippo.ehviewer.ui.tools.LocalDialogState +import com.hippo.ehviewer.util.toIntOrDefault import com.jamal.composeprefs3.ui.ifNotNullThen import com.jamal.composeprefs3.ui.ifTrueThen import eu.kanade.tachiyomi.util.lang.launchIO @@ -107,7 +121,8 @@ fun SearchBarScreen( title: String? = null, searchFieldState: TextFieldState, searchFieldHint: String? = null, - showLanguageFilter: Boolean = false, + advancedOption: AdvancedSearchOption? = null, + onAdvancedOptionChanged: (AdvancedSearchOption) -> Unit = {}, showSearchFab: Boolean = false, onApplySearch: suspend (String) -> Unit, onSearchExpanded: () -> Unit, @@ -321,7 +336,7 @@ fun SearchBarScreen( } }, ) { - if (showLanguageFilter) { + if (advancedOption != null) { var languageFilter by Settings.languageFilter.asMutableState() val languages = remember { GalleryInfo.S_LANG_TAGS.map { tag -> @@ -334,7 +349,11 @@ fun SearchBarScreen( } }.toTypedArray() } - Row(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) { + Row( + modifier = Modifier.horizontalScroll(rememberScrollState()) + .padding(horizontal = 16.dp, vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { val any = stringResource(id = R.string.any) val languageStr = stringResource(id = R.string.key_language) FilterChip( @@ -355,6 +374,132 @@ fun SearchBarScreen( ) }, ) + val pageErr1 = stringResource(R.string.search_sp_err1) + val pageErr2 = stringResource(R.string.search_sp_err2) + val pages = stringResource(id = R.string.key_pages) + val pagesText = remember(advancedOption.fromPage, advancedOption.toPage) { + advancedOption.run { + val hasFrom = fromPage > 0 + val hasTo = toPage > 0 + if (hasFrom && hasTo) { + "$fromPage - $toPage P" + } else if (hasFrom) { + "$fromPage+ P" + } else if (hasTo) { + "$toPage- P" + } else { + pages + } + } + } + FilterChip( + selected = advancedOption.fromPage != 0 || advancedOption.toPage != 0, + onClick = { + scope.launch { + val (from, to) = dialogState.awaitResult( + initial = advancedOption.fromPage to advancedOption.toPage, + title = R.string.key_pages, + invalidator = { (from, to) -> + if (to != 0 && to < 10) { + pageErr1 + } else if (from != 0 && to != 0 && to - from < 20) { + pageErr2 + } else { + null + } + }, + ) { error -> + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Row(verticalAlignment = Alignment.CenterVertically) { + OutlinedTextField( + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword), + value = expectedValue.first.takeIf { it > 0 }?.toString().orEmpty(), + onValueChange = { + expectedValue = + expectedValue.copy(first = it.toIntOrDefault(0).coerceAtLeast(0)) + }, + modifier = Modifier.width(112.dp).padding(16.dp), + singleLine = true, + isError = error != null, + ) + Text(text = stringResource(id = R.string.search_sp_to)) + OutlinedTextField( + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword), + value = expectedValue.second.takeIf { it > 0 }?.toString().orEmpty(), + onValueChange = { + expectedValue = + expectedValue.copy(second = it.toIntOrDefault(0).coerceAtLeast(0)) + }, + modifier = Modifier.width(112.dp).padding(16.dp), + singleLine = true, + isError = error != null, + ) + Text(text = stringResource(id = R.string.search_sp_suffix)) + } + if (error != null) { + ListItem( + headlineContent = { Text(text = error) }, + leadingContent = { + IconFix(imageVector = Icons.Default.Info, contentDescription = null) + }, + colors = ListItemDefaults.colors( + headlineColor = MaterialTheme.colorScheme.error, + leadingIconColor = MaterialTheme.colorScheme.error, + ), + ) + } + } + } + onAdvancedOptionChanged(advancedOption.copy(fromPage = from, toPage = to)) + } + }, + label = { Text(text = pagesText) }, + ) + val minRatingItems = stringArrayResource(id = R.array.search_min_rating) + val minRatingStr = stringResource(id = R.string.search_sr) + val minRatingIndex = (advancedOption.minRating - 1).coerceAtLeast(0) + FilterChip( + selected = advancedOption.minRating != 0, + onClick = { + scope.launch { + val selected = + dialogState.showSingleChoice(*minRatingItems, selected = minRatingIndex) + onAdvancedOptionChanged( + advancedOption.copy(minRating = if (selected == 0) 0 else selected + 1), + ) + } + }, + label = { Text(text = minRatingStr + minRatingItems[minRatingIndex]) }, + ) + fun checked(bit: Int) = advancedOption.advanceSearch and bit != 0 + fun AdvancedSearchOption.inv(bit: Int) = + onAdvancedOptionChanged(copy(advanceSearch = advanceSearch xor bit)) + FilterChip( + selected = checked(AdvanceTable.SH), + onClick = { advancedOption.inv(AdvanceTable.SH) }, + label = { Text(text = stringResource(id = R.string.search_sh)) }, + ) + FilterChip( + selected = checked(AdvanceTable.STO), + onClick = { advancedOption.inv(AdvanceTable.STO) }, + label = { Text(text = stringResource(id = R.string.search_sto)) }, + ) + val disableFilter = stringResource(id = R.string.search_sf) + FilterChip( + selected = checked(AdvanceTable.SFL), + onClick = { advancedOption.inv(AdvanceTable.SFL) }, + label = { Text(text = disableFilter + stringResource(id = R.string.search_sfl)) }, + ) + FilterChip( + selected = checked(AdvanceTable.SFU), + onClick = { advancedOption.inv(AdvanceTable.SFU) }, + label = { Text(text = disableFilter + stringResource(id = R.string.search_sfu)) }, + ) + FilterChip( + selected = checked(AdvanceTable.SFT), + onClick = { advancedOption.inv(AdvanceTable.SFT) }, + label = { Text(text = disableFilter + stringResource(id = R.string.search_sft)) }, + ) } } LazyColumn( diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt index 030411577..38f76e9b2 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt @@ -109,9 +109,16 @@ class DialogState { content = { block(realContinuation) } } - suspend fun awaitResult(initial: R, @StringRes title: Int? = null, block: @Composable DialogScope.() -> Unit): R { + suspend fun awaitResult( + initial: R, + @StringRes title: Int? = null, + invalidator: (suspend (R) -> String?)? = null, + block: @Composable DialogScope.(String?) -> Unit, + ): R { return dialog { cont -> + val coroutineScope = rememberCoroutineScope() val state = remember(cont) { mutableStateOf(initial) } + var error by remember(cont) { mutableStateOf(null) } val impl = remember(cont) { object : DialogScope { override var expectedValue by state @@ -120,12 +127,21 @@ class DialogState { AlertDialog( onDismissRequest = { cont.cancel() }, confirmButton = { - TextButton(onClick = { cont.resume(state.value) }) { + TextButton(onClick = { + if (invalidator == null) { + cont.resume(state.value) + } else { + coroutineScope.launch { + error = invalidator.invoke(state.value) + error ?: cont.resume(state.value) + } + } + }) { Text(text = stringResource(id = android.R.string.ok)) } }, title = title.ifNotNullThen { Text(text = stringResource(id = title!!)) }, - text = { block(impl) }, + text = { block(impl, error) }, ) } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 5d2132906..92912165b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -86,8 +86,8 @@ 指定标签 启用高级选项 一般搜索:就是搜索。\n\n订阅搜素:在订阅中搜索。\n\n指定上传者:列出该上传者上传的画廊。其他选项会被忽略。\n\n指定标签:列出含有该标签的画廊。其他选项会被忽略。 - 仅被删除的画廊 - 仅有种子的画廊 + 已删除 + 有种子 最低评分: 页数: 到 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index cbef9dec3..792b0fc8b 100755 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -101,8 +101,8 @@ 一般搜索:就是搜索。\n\n訂閲搜素:在訂閲中搜索。\n\n指定上傳者:列出該上傳者上傳的畫廊。其他選項會被忽略。\n\n指定標籤:列出含有該標籤的畫廊。其他選項會被忽略。 - 僅顯示被刪除的畫廊 - 僅顯示有種子的畫廊 + 已刪除 + 有種子 最低評分: 頁數: 到 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 9a91b22ce..ec5cd675c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -86,8 +86,8 @@ 指定標籤 啟用進階選項 一般搜尋:就是搜尋。\n\n訂閱搜尋:在訂閱中搜尋。\n\n指定上傳者:列出該上傳者上傳的圖庫。其他選項會被忽略。\n\n指定標籤:列出含有該標籤的圖庫。其他選項會被忽略。 - 只顯示已被移除的圖庫 - 只顯示有種子的圖庫 + 已被移除 + 有種子 最低評分: 頁數: 到 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 919c928fc..624e465ed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -124,8 +124,8 @@ Specify tag Enable advance options Normal search: Simply search.\n\nSubscription search: Search in subscriptions.\n\nSpecify uploader: Search for galleries uploaded by the specified uploader. All other options will be ignored.\n\nSpecify tag: Search for galleries that contain the specified tag. Only one tag accepted. Other options will be ignored. - Expunged Galleries only - Require Gallery Torrent + Expunged + Has Torrent Minimum Rating: Pages: to