This commit is contained in:
HapeLee
2026-05-27 23:52:27 +08:00
parent 6278f26839
commit 02dca3edc1
4 changed files with 65 additions and 31 deletions
@@ -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 <T> HomepageLayoutSheet(
title = stringResource(R.string.homepage_layout_settings),
) {
Column {
DropdownListSettingItem(
CompactDropdownSettingItem(
title = stringResource(R.string.homepage_layout_mode),
selectedValue = layoutMode.toString(),
displayEntries = arrayOf(
@@ -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<HomepageModuleUi>,
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. 头部 (全宽)
@@ -104,9 +104,6 @@ class HomepageViewModel(
private val _layoutConfigCache = MutableStateFlow<Map<String, Map<String, String>>>(emptyMap())
private val _pendingEnabled = MutableStateFlow<Map<String, Boolean>>(emptyMap())
private val _pendingUserModules = MutableStateFlow<List<ModuleItem>>(emptyList())
private val _exploreKindsCache =
MutableStateFlow<Map<String, List<Pair<String, String>>>>(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<Triple<String, String, String?>>(bookshelf.size)
val nameAuthorKeys = HashSet<Pair<String, String>>(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<Pair<String, String>> =
_exploreKindsCache.value[sourceUrl].orEmpty()
fun getSourceExploreKinds(sourceUrl: String): List<Pair<String, String>> = emptyList()
fun updateModule(globalId: String, def: ModuleDef) {
viewModelScope.launch {
@@ -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) {