diff --git a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageLayoutSheet.kt b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageLayoutSheet.kt index 79dbe6104..62b87dd0d 100644 --- a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageLayoutSheet.kt +++ b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageLayoutSheet.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.legado.app.R import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet +import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem @Composable @@ -39,7 +40,7 @@ fun HomepageLayoutSheet( title = stringResource(R.string.homepage_layout_settings), ) { Column { - DropdownListSettingItem( + CompactDropdownSettingItem( title = stringResource(R.string.homepage_layout_mode), selectedValue = layoutMode.toString(), displayEntries = arrayOf( diff --git a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageScreen.kt b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageScreen.kt index 2db624614..bb7809da6 100644 --- a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageScreen.kt +++ b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageScreen.kt @@ -72,7 +72,9 @@ import io.legado.app.ui.main.homepage.modules.GridRankingModule import io.legado.app.ui.main.homepage.modules.HomepageModuleSkeleton import io.legado.app.ui.main.homepage.modules.RankingModule import io.legado.app.ui.main.homepage.modules.WaterfallItem +import io.legado.app.ui.config.themeConfig.ThemeConfig import io.legado.app.ui.theme.LegadoTheme +import io.legado.app.ui.theme.adaptiveContentPaddingBookshelf import io.legado.app.ui.widget.components.AppPullToRefresh import io.legado.app.ui.widget.components.AppScaffold import io.legado.app.ui.widget.components.LoadMoreFooter @@ -84,6 +86,7 @@ import io.legado.app.ui.widget.components.card.GlassCard import io.legado.app.ui.widget.components.icon.AppIcon import io.legado.app.ui.widget.components.tabRow.AppTabRow import io.legado.app.ui.widget.components.EmptyMessage +import io.legado.app.ui.widget.components.icon.AppIcons import io.legado.app.ui.widget.components.text.AppText import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults @@ -171,12 +174,12 @@ fun HomepageScreen( actions = { TopBarActionButton( onClick = { viewModel.toggleConfigMode() }, - imageVector = Icons.Default.GridView, + imageVector = AppIcons.MoreCircle, contentDescription = "Layout Settings", ) TopBarActionButton( onClick = { viewModel.toggleManageMode() }, - imageVector = Icons.Default.Settings, + imageVector = AppIcons.Settings, contentDescription = "Manage Modules", ) }, @@ -200,14 +203,14 @@ fun HomepageScreen( AppPullToRefresh( isRefreshing = uiState.isRefreshing, onRefresh = { viewModel.onRefresh() }, - modifier = Modifier - .fillMaxSize() - .padding(paddingValues), + modifier = Modifier.fillMaxSize(), ) { if (selectedSets.isEmpty()) { EmptyMessage( messageResId = R.string.homepage_no_source_sets_selected, - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .padding(paddingValues), ) } else { HorizontalPager( @@ -231,6 +234,7 @@ fun HomepageScreen( modules = sourceModules, viewModel = viewModel, modifier = Modifier.fillMaxSize(), + paddingValues = paddingValues, onErrorClick = { errorMsg = it }, sharedTransitionScope = sharedTransitionScope, animatedVisibilityScope = animatedVisibilityScope, @@ -344,6 +348,7 @@ private fun ModuleList( modules: List, viewModel: HomepageViewModel, modifier: Modifier = Modifier, + paddingValues: PaddingValues = PaddingValues(0.dp), gridState: LazyStaggeredGridState = rememberLazyStaggeredGridState(), sharedTransitionScope: SharedTransitionScope? = null, animatedVisibilityScope: AnimatedVisibilityScope? = null, @@ -381,7 +386,11 @@ private fun ModuleList( modifier = modifier, verticalItemSpacing = 16.dp, horizontalArrangement = Arrangement.spacedBy(12.dp), - contentPadding = PaddingValues(start = 16.dp, end = 16.dp, bottom = 80.dp), + contentPadding = adaptiveContentPaddingBookshelf( + top = paddingValues.calculateTopPadding(), + bottom = if (ThemeConfig.useFloatingBottomBar || ThemeConfig.enableBlur) 120.dp else 8.dp, + horizontal = 4.dp + ), ) { processedModules.forEach { moduleUi -> // 1. 头部 (全宽) diff --git a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageViewModel.kt b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageViewModel.kt index b916ef690..33995061f 100644 --- a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageViewModel.kt @@ -104,9 +104,6 @@ class HomepageViewModel( private val _layoutConfigCache = MutableStateFlow>>(emptyMap()) private val _pendingEnabled = MutableStateFlow>(emptyMap()) private val _pendingUserModules = MutableStateFlow>(emptyList()) - private val _exploreKindsCache = - MutableStateFlow>>>(emptyMap()) - // 2. 数据库同步流 private val localModulesFlow = gateway.flowEnabled() val allModulesCache = @@ -228,27 +225,44 @@ class HomepageViewModel( rawModulesFlow, _bookshelf ) { modules, bookshelf -> - modules.map { module -> - val state = module.state - if (state is ModuleLoadState.Loaded) { - module.copy( - state = state.copy( + if (bookshelf.isEmpty()) { + modules.map { module -> + val state = module.state + if (state is ModuleLoadState.Loaded) { + module.copy(state = state.copy( books = state.books.map { item -> - item.copy( - shelfState = resolveBookShelfStateUseCase.execute( - name = item.book.name, - author = item.book.author, - url = item.book.bookUrl, - shelf = bookshelf - ) - ) + if (item.shelfState == BookShelfState.NOT_IN_SHELF) item + else item.copy(shelfState = BookShelfState.NOT_IN_SHELF) }.toImmutableList() - ) - ) - } else { - module + )) + } else module + }.toImmutableList() + } else { + val exactKeys = HashSet>(bookshelf.size) + val nameAuthorKeys = HashSet>(bookshelf.size) + for (key in bookshelf) { + exactKeys.add(Triple(key.name, key.author, key.url)) + nameAuthorKeys.add(key.name to key.author) } - }.toImmutableList() + modules.map { module -> + val state = module.state + if (state is ModuleLoadState.Loaded) { + module.copy(state = state.copy( + books = state.books.map { item -> + val bookTriple = Triple(item.book.name, item.book.author, item.book.bookUrl) + val newShelfState = when { + bookTriple in exactKeys -> BookShelfState.IN_SHELF + (item.book.name to item.book.author) in nameAuthorKeys -> + BookShelfState.SAME_NAME_AUTHOR + else -> BookShelfState.NOT_IN_SHELF + } + if (item.shelfState == newShelfState) item + else item.copy(shelfState = newShelfState) + }.toImmutableList() + )) + } else module + }.toImmutableList() + } } // 5. 最终 UI 状态 @@ -308,6 +322,10 @@ class HomepageViewModel( allModulesCache.collect { modules -> val dbIds = modules.map { it.id }.toSet() _pendingUserModules.update { pending -> pending.filter { it.id !in dbIds } } + _moduleContentStates.update { states -> + if (states.keys.any { it !in dbIds }) states.filterKeys { it in dbIds } + else states + } } } @@ -614,8 +632,7 @@ class HomepageViewModel( } } - fun getSourceExploreKinds(sourceUrl: String): List> = - _exploreKindsCache.value[sourceUrl].orEmpty() + fun getSourceExploreKinds(sourceUrl: String): List> = emptyList() fun updateModule(globalId: String, def: ModuleDef) { viewModelScope.launch { diff --git a/app/src/main/java/io/legado/app/ui/widget/components/icon/AppIcons.kt b/app/src/main/java/io/legado/app/ui/widget/components/icon/AppIcons.kt index 8ffa9d51c..e5b9b059c 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/icon/AppIcons.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/icon/AppIcons.kt @@ -13,6 +13,7 @@ import androidx.compose.material.icons.filled.FilterList import androidx.compose.material.icons.filled.History import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.LocationSearching +import androidx.compose.material.icons.filled.MoreHoriz import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MyLocation import androidx.compose.material.icons.filled.Person @@ -40,6 +41,7 @@ import top.yukonga.miuix.kmp.icon.extended.Edit import top.yukonga.miuix.kmp.icon.extended.Favorites import top.yukonga.miuix.kmp.icon.extended.Filter import top.yukonga.miuix.kmp.icon.extended.More +import top.yukonga.miuix.kmp.icon.extended.MoreCircle import top.yukonga.miuix.kmp.icon.extended.Notes import top.yukonga.miuix.kmp.icon.extended.Pin import top.yukonga.miuix.kmp.icon.extended.Refresh @@ -105,6 +107,11 @@ object AppIcons { @Composable get() = if (isMiuix) MiuixIcons.Refresh else Icons.Default.Replay + val MoreCircle: ImageVector + @Composable + get() = if (isMiuix) MiuixIcons.MoreCircle else Icons.Default.MoreHoriz + + @Composable fun mainDestination(destination: MainDestination, selected: Boolean): ImageVector { return when (destination) {