Просмотр исходного кода

[优化] 发现页可以在添加进书架后立即标记,与增加发现页内分组搜索

HapeLee 11 месяцев назад
Родитель
Сommit
b7323b54ed

+ 32 - 13
app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt

@@ -93,7 +93,9 @@ import io.legado.app.ui.widget.components.AnimatedTextButton
 import io.legado.app.ui.widget.components.AnimatedTextLine
 import io.legado.app.ui.widget.components.Cover
 import io.legado.app.ui.widget.components.EmptyMessageView
+import io.legado.app.ui.widget.components.SearchBarSection
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
 import org.koin.androidx.compose.koinViewModel
 
 @SuppressLint("LocalContextConfigurationRead", "ConfigurationScreenWidthHeight")
@@ -216,6 +218,7 @@ fun ExploreShowScreen(
 
     if (showKindSheet) {
         val scrollState = rememberScrollState()
+        var kindQuery by remember { mutableStateOf("") }
         val sheetState = rememberModalBottomSheetState(
             skipPartiallyExpanded = true,
             confirmValueChange = { newValue ->
@@ -231,16 +234,30 @@ fun ExploreShowScreen(
                     .fillMaxWidth()
                     .heightIn(max = LocalConfiguration.current.screenHeightDp.dp * 0.8f)
                     .verticalScroll(scrollState)
-                    .padding(12.dp)
             ) {
-                Text(
-                    "选择分类",
-                    style = MaterialTheme.typography.titleMedium,
-                    modifier = Modifier.padding(bottom = 8.dp)
+
+                var kindQuery by remember { mutableStateOf("") }
+
+                SearchBarSection(
+                    query = kindQuery,
+                    onQueryChange = { kindQuery = it },
+                    placeholder = "选择或搜索分类",
+                    backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh
                 )
 
-                FlowRow(modifier = Modifier.fillMaxWidth()) {
-                    kinds.forEach { kind ->
+                val filteredKinds = remember(kindQuery, kinds) {
+                    if (kindQuery.isBlank()) kinds
+                    else kinds.filter { kind ->
+                        kind.title.contains(kindQuery, ignoreCase = true) ||
+                                (kind.url?.contains(kindQuery, ignoreCase = true) == true)
+                    }
+                }
+
+                FlowRow(
+                    modifier = Modifier.fillMaxWidth()
+                        .padding(16.dp)
+                ) {
+                    filteredKinds.forEach { kind ->
                         KindListItem(
                             kind = kind,
                             currentTitle = selectedTitle ?: title,
@@ -295,10 +312,9 @@ fun ExploreShowScreen(
                             items = books,
                             key = { it.bookUrl }
                         ) { book ->
-                            val shelfState = viewModel.getCurrentBookShelfState(book)
                             ExploreBookGridItem(
                                 book = book,
-                                shelfState = shelfState,
+                                shelfState = viewModel.getBookShelfStateFlow(book),
                                 onClick = { onBookClick(book) },
                                 modifier = Modifier.animateItem()
                             )
@@ -325,7 +341,7 @@ fun ExploreShowScreen(
                             val shelfState = viewModel.getCurrentBookShelfState(book)
                             ExploreBookItem(
                                 book = book,
-                                shelfState = shelfState,
+                                shelfState = viewModel.getBookShelfStateFlow(book),
                                 onClick = { onBookClick(book) },
                                 modifier = Modifier.animateItem()
                             )
@@ -372,7 +388,7 @@ fun KindListItem(
         modifier = Modifier
             .then(
                 if (!isClickable) Modifier.fillMaxWidth()
-                else Modifier.fillMaxWidth(1 / 3f)
+                else Modifier.fillMaxWidth(1f / 3f)
             )
             .padding(horizontal = 4.dp)
     )
@@ -472,10 +488,11 @@ fun ExploreTopBar(
 @Composable
 fun ExploreBookItem(
     book: SearchBook,
-    shelfState: BookShelfState,
+    shelfState: Flow<BookShelfState>,
     onClick: () -> Unit,
     modifier: Modifier = Modifier
 ) {
+    val shelfState by shelfState.collectAsState(initial = BookShelfState.NOT_IN_SHELF)
 
     val badge: (@Composable RowScope.() -> Unit)?
             = when (shelfState) {
@@ -584,10 +601,12 @@ fun ExploreBookItem(
 fun ExploreBookGridItem(
     book: SearchBook,
     onClick: () -> Unit,
-    shelfState: BookShelfState,
+    shelfState: Flow<BookShelfState>,
     modifier: Modifier = Modifier
 ) {
 
+    val shelfState by shelfState.collectAsState(initial = BookShelfState.NOT_IN_SHELF)
+
     val badgeText: String? = when (shelfState) {
         BookShelfState.IN_SHELF -> "已在书架"
         BookShelfState.SAME_NAME_AUTHOR -> "同名书籍"

+ 9 - 0
app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt

@@ -11,11 +11,14 @@ import io.legado.app.help.config.AppConfig
 import io.legado.app.model.BookShelfState
 import io.legado.app.ui.book.search.BookKey
 import io.legado.app.utils.exploreLayoutGrid
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import splitties.init.appCtx
@@ -198,6 +201,12 @@ class ExploreShowViewModel(
         }
     }
 
+    fun getBookShelfStateFlow(item: SearchBook): Flow<BookShelfState> {
+        return _bookshelf.map { shelf ->
+            getBookShelfState(item, shelf)
+        }.distinctUntilChanged()
+    }
+
     private fun getBookShelfState(item: SearchBook, shelf: Set<BookKey>): BookShelfState {
         val exactMatch = shelf.any { it.name == item.name && it.author == item.author && it.url == item.bookUrl }
         if (exactMatch) return BookShelfState.IN_SHELF

+ 2 - 1
app/src/main/java/io/legado/app/ui/widget/components/AnimatedTextButton.kt

@@ -6,6 +6,7 @@ import androidx.compose.animation.core.tween
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
 import androidx.compose.animation.togetherWith
+import androidx.compose.material3.ContainedLoadingIndicator
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.LoadingIndicator
 import androidx.compose.material3.Text
@@ -36,7 +37,7 @@ fun AnimatedTextButton(
             label = "ButtonLoading"
         ) { loading ->
             if (loading) {
-                LoadingIndicator()
+                ContainedLoadingIndicator()
             } else {
                 Text(text)
             }

+ 3 - 2
app/src/main/java/io/legado/app/ui/widget/components/SearchBarSection.kt

@@ -24,14 +24,15 @@ import androidx.compose.ui.unit.dp
 fun SearchBarSection(
     query: String,
     onQueryChange: (String) -> Unit,
-    placeholder: String = "搜索书名"
+    placeholder: String = "搜索书名",
+    backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow
 ) {
     Surface(
         modifier = Modifier
             .fillMaxWidth()
             .padding(horizontal = 16.dp, vertical = 8.dp),
         shape = RoundedCornerShape(32.dp),
-        color = MaterialTheme.colorScheme.surfaceContainerLow
+        color = backgroundColor
     ) {
 
         TextField(