소스 검색

[修复] 修复偶现的主页面崩溃问题

HapeLee 7 달 전
부모
커밋
46a11de976
1개의 변경된 파일과 141개의 추가작업 그리고 9개의 파일을 삭제
  1. 141 9
      app/src/main/java/io/legado/app/ui/main/MainScreen.kt

+ 141 - 9
app/src/main/java/io/legado/app/ui/main/MainScreen.kt

@@ -1,5 +1,7 @@
 package io.legado.app.ui.main
 
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
@@ -8,10 +10,12 @@ import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.pager.HorizontalPager
 import androidx.compose.foundation.pager.rememberPagerState
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.MenuOpen
+import androidx.compose.material.icons.filled.Check
 import androidx.compose.material.icons.filled.Menu
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.material3.Badge
@@ -33,11 +37,16 @@ import androidx.compose.material3.WideNavigationRailValue
 import androidx.compose.material3.rememberWideNavigationRailState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -48,18 +57,24 @@ import io.legado.app.ui.book.info.BookInfoActivity
 import io.legado.app.ui.book.search.SearchActivity
 import io.legado.app.ui.config.mainConfig.MainConfig
 import io.legado.app.ui.main.bookshelf.BookshelfScreen
+import io.legado.app.ui.main.bookshelf.BookshelfViewModel
 import io.legado.app.ui.main.explore.ExploreScreen
 import io.legado.app.ui.main.my.MyScreen
 import io.legado.app.ui.main.rss.RssScreen
 import io.legado.app.ui.theme.regularHazeEffect
 import io.legado.app.ui.widget.components.AppScaffold
 import io.legado.app.ui.widget.components.GlassDefaults
+import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
+import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 import io.legado.app.utils.startActivity
 import io.legado.app.utils.startActivityForBook
 import kotlinx.coroutines.launch
 import org.koin.androidx.compose.koinViewModel
 
-@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+@OptIn(
+    ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class,
+    ExperimentalFoundationApi::class
+)
 @Composable
 fun MainScreen(
     viewModel: MainViewModel = koinViewModel(),
@@ -69,6 +84,9 @@ fun MainScreen(
     val uiState by viewModel.state.collectAsStateWithLifecycle()
     val coroutineScope = rememberCoroutineScope()
 
+    val bookshelfViewModel: BookshelfViewModel = koinViewModel()
+    val bookshelfUiState by bookshelfViewModel.uiState.collectAsState()
+
     val hazeState = remember { HazeState() }
     val destinations = remember(MainConfig.showDiscovery, MainConfig.showRSS) {
         MainDestination.mainDestinations.filter {
@@ -139,6 +157,9 @@ fun MainScreen(
                 val labelVisibilityMode = MainConfig.labelVisibilityMode
                 destinations.forEachIndexed { index, destination ->
                     val selected = pagerState.targetPage == index
+                    var showGroupMenu by remember { mutableStateOf(false) }
+                    val haptic = LocalHapticFeedback.current
+
                     WideNavigationRailItem(
                         railExpanded = navState.targetValue == WideNavigationRailValue.Expanded,
                         selected = selected,
@@ -148,7 +169,57 @@ fun MainScreen(
                             }
                         },
                         icon = {
-                            NavigationIcon(destination, selected, uiState.upBooksCount)
+                            Box {
+                                NavigationIcon(
+                                    destination = destination,
+                                    selected = selected,
+                                    upBooksCount = uiState.upBooksCount,
+                                    modifier = if (destination == MainDestination.Bookshelf) {
+                                        Modifier.combinedClickable(
+                                            onClick = {
+                                                coroutineScope.launch {
+                                                    pagerState.animateScrollToPage(index)
+                                                }
+                                            },
+                                            onLongClick = {
+                                                haptic.performHapticFeedback(HapticFeedbackType.LongPress)
+                                                showGroupMenu = true
+                                            }
+                                        )
+                                    } else Modifier
+                                )
+
+                                if (destination == MainDestination.Bookshelf && showGroupMenu) {
+                                    RoundDropdownMenu(
+                                        expanded = showGroupMenu,
+                                        onDismissRequest = { showGroupMenu = false }
+                                    ) { dismiss ->
+                                        bookshelfUiState.groups.forEachIndexed { groupIndex, group ->
+                                            RoundDropdownMenuItem(
+                                                text = { Text(group.groupName) },
+                                                onClick = {
+                                                    coroutineScope.launch {
+                                                        if (pagerState.currentPage != index) {
+                                                            pagerState.scrollToPage(index)
+                                                        }
+                                                        bookshelfViewModel.changeGroup(group.groupId)
+                                                        dismiss()
+                                                    }
+                                                },
+                                                trailingIcon = {
+                                                    if (bookshelfUiState.selectedGroupIndex == groupIndex) {
+                                                        Icon(
+                                                            Icons.Default.Check,
+                                                            null,
+                                                            modifier = Modifier.size(18.dp)
+                                                        )
+                                                    }
+                                                }
+                                            )
+                                        }
+                                    }
+                                }
+                            }
                         },
                         label = if (labelVisibilityMode != "unlabeled") {
                             { Text(stringResource(destination.labelId)) }
@@ -181,6 +252,9 @@ fun MainScreen(
                         }
                         destinations.forEachIndexed { index, destination ->
                             val selected = pagerState.targetPage == index
+                            var showGroupMenu by remember { mutableStateOf(false) }
+                            val haptic = LocalHapticFeedback.current
+
                             NavigationBarItem(
                                 selected = selected,
                                 onClick = {
@@ -189,7 +263,61 @@ fun MainScreen(
                                     }
                                 },
                                 icon = {
-                                    NavigationIcon(destination, selected, uiState.upBooksCount)
+                                    Box {
+                                        NavigationIcon(
+                                            destination = destination,
+                                            selected = selected,
+                                            upBooksCount = uiState.upBooksCount,
+                                            modifier = if (destination == MainDestination.Bookshelf) {
+                                                Modifier.combinedClickable(
+                                                    onClick = {
+                                                        coroutineScope.launch {
+                                                            pagerState.animateScrollToPage(index)
+                                                        }
+                                                    },
+                                                    onLongClick = {
+                                                        haptic.performHapticFeedback(
+                                                            HapticFeedbackType.LongPress
+                                                        )
+                                                        showGroupMenu = true
+                                                    }
+                                                )
+                                            } else Modifier
+                                        )
+
+                                        if (destination == MainDestination.Bookshelf && showGroupMenu) {
+                                            RoundDropdownMenu(
+                                                expanded = showGroupMenu,
+                                                onDismissRequest = { showGroupMenu = false }
+                                            ) { dismiss ->
+                                                bookshelfUiState.groups.forEachIndexed { groupIndex, group ->
+                                                    RoundDropdownMenuItem(
+                                                        text = { Text(group.groupName) },
+                                                        onClick = {
+                                                            coroutineScope.launch {
+                                                                if (pagerState.currentPage != index) {
+                                                                    pagerState.scrollToPage(index)
+                                                                }
+                                                                bookshelfViewModel.changeGroup(
+                                                                    group.groupId
+                                                                )
+                                                                dismiss()
+                                                            }
+                                                        },
+                                                        trailingIcon = {
+                                                            if (bookshelfUiState.selectedGroupIndex == groupIndex) {
+                                                                Icon(
+                                                                    Icons.Default.Check,
+                                                                    null,
+                                                                    modifier = Modifier.size(18.dp)
+                                                                )
+                                                            }
+                                                        }
+                                                    )
+                                                }
+                                            }
+                                        }
+                                    }
                                 },
                                 colors = NavigationBarItemDefaults.colors(
                                     indicatorColor = GlassDefaults.glassColor(
@@ -215,7 +343,8 @@ fun MainScreen(
                     userScrollEnabled = true,
                     beyondViewportPageCount = 3
                 ) { page ->
-                    when (destinations[page]) {
+                    val destination = destinations.getOrNull(page) ?: return@HorizontalPager
+                    when (destination) {
                         MainDestination.Bookshelf -> BookshelfScreen(
                             onBookClick = { book ->
                                 context.startActivityForBook(book)
@@ -248,14 +377,17 @@ fun MainScreen(
 private fun NavigationIcon(
     destination: MainDestination,
     selected: Boolean,
-    upBooksCount: Int
+    upBooksCount: Int,
+    modifier: Modifier = Modifier
 ) {
     val icon = if (selected) destination.selectedIcon else destination.icon
-    if (destination == MainDestination.Bookshelf && upBooksCount > 0) {
-        BadgedBox(badge = { Badge { Text(upBooksCount.toString()) } }) {
+    Box(modifier = modifier) {
+        if (destination == MainDestination.Bookshelf && upBooksCount > 0) {
+            BadgedBox(badge = { Badge { Text(upBooksCount.toString()) } }) {
+                Icon(icon, contentDescription = null)
+            }
+        } else {
             Icon(icon, contentDescription = null)
         }
-    } else {
-        Icon(icon, contentDescription = null)
     }
 }