|
@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Arrangement
|
|
|
import androidx.compose.foundation.layout.Box
|
|
import androidx.compose.foundation.layout.Box
|
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.Column
|
|
|
import androidx.compose.foundation.layout.FlowRow
|
|
import androidx.compose.foundation.layout.FlowRow
|
|
|
|
|
+import androidx.compose.foundation.layout.FlowRowScope
|
|
|
import androidx.compose.foundation.layout.IntrinsicSize
|
|
import androidx.compose.foundation.layout.IntrinsicSize
|
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.Row
|
|
@@ -86,6 +87,12 @@ import androidx.compose.ui.text.style.TextAlign
|
|
|
import androidx.compose.ui.text.style.TextOverflow
|
|
import androidx.compose.ui.text.style.TextOverflow
|
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.compose.ui.unit.dp
|
|
|
import androidx.compose.ui.unit.sp
|
|
import androidx.compose.ui.unit.sp
|
|
|
|
|
+import dev.chrisbanes.haze.HazeProgressive
|
|
|
|
|
+import dev.chrisbanes.haze.HazeState
|
|
|
|
|
+import dev.chrisbanes.haze.hazeEffect
|
|
|
|
|
+import dev.chrisbanes.haze.hazeSource
|
|
|
|
|
+import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
|
|
|
|
+import dev.chrisbanes.haze.materials.HazeMaterials
|
|
|
import io.legado.app.data.entities.SearchBook
|
|
import io.legado.app.data.entities.SearchBook
|
|
|
import io.legado.app.data.entities.rule.ExploreKind
|
|
import io.legado.app.data.entities.rule.ExploreKind
|
|
|
import io.legado.app.model.BookShelfState
|
|
import io.legado.app.model.BookShelfState
|
|
@@ -98,7 +105,7 @@ import kotlinx.coroutines.flow.Flow
|
|
|
import org.koin.androidx.compose.koinViewModel
|
|
import org.koin.androidx.compose.koinViewModel
|
|
|
|
|
|
|
|
@SuppressLint("LocalContextConfigurationRead", "ConfigurationScreenWidthHeight")
|
|
@SuppressLint("LocalContextConfigurationRead", "ConfigurationScreenWidthHeight")
|
|
|
-@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
|
|
|
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
|
|
|
@Composable
|
|
@Composable
|
|
|
fun ExploreShowScreen(
|
|
fun ExploreShowScreen(
|
|
|
title: String,
|
|
title: String,
|
|
@@ -128,7 +135,7 @@ fun ExploreShowScreen(
|
|
|
val isGridMode = layoutState == 1
|
|
val isGridMode = layoutState == 1
|
|
|
var showGridCountSheet by remember { mutableStateOf(false) }
|
|
var showGridCountSheet by remember { mutableStateOf(false) }
|
|
|
val gridColumnCount by viewModel.gridCount.collectAsState()
|
|
val gridColumnCount by viewModel.gridCount.collectAsState()
|
|
|
-
|
|
|
|
|
|
|
+ val hazeState = remember { HazeState() }
|
|
|
val shouldLoadMoreList = remember {
|
|
val shouldLoadMoreList = remember {
|
|
|
derivedStateOf {
|
|
derivedStateOf {
|
|
|
val total = listState.layoutInfo.totalItemsCount
|
|
val total = listState.layoutInfo.totalItemsCount
|
|
@@ -178,12 +185,14 @@ fun ExploreShowScreen(
|
|
|
|
|
|
|
|
ModalBottomSheet(
|
|
ModalBottomSheet(
|
|
|
sheetState = sheetState,
|
|
sheetState = sheetState,
|
|
|
|
|
+ dragHandle = null,
|
|
|
onDismissRequest = { showGridCountSheet = false }
|
|
onDismissRequest = { showGridCountSheet = false }
|
|
|
) {
|
|
) {
|
|
|
Column(
|
|
Column(
|
|
|
modifier = Modifier
|
|
modifier = Modifier
|
|
|
.fillMaxWidth()
|
|
.fillMaxWidth()
|
|
|
- .padding(24.dp)
|
|
|
|
|
|
|
+ .hazeEffect(state = hazeState, style = HazeMaterials.regular())
|
|
|
|
|
+ .padding(16.dp)
|
|
|
) {
|
|
) {
|
|
|
|
|
|
|
|
Text(
|
|
Text(
|
|
@@ -217,7 +226,6 @@ fun ExploreShowScreen(
|
|
|
|
|
|
|
|
if (showKindSheet) {
|
|
if (showKindSheet) {
|
|
|
val scrollState = rememberScrollState()
|
|
val scrollState = rememberScrollState()
|
|
|
- var kindQuery by remember { mutableStateOf("") }
|
|
|
|
|
val sheetState = rememberModalBottomSheetState(
|
|
val sheetState = rememberModalBottomSheetState(
|
|
|
skipPartiallyExpanded = true,
|
|
skipPartiallyExpanded = true,
|
|
|
confirmValueChange = { newValue ->
|
|
confirmValueChange = { newValue ->
|
|
@@ -226,12 +234,14 @@ fun ExploreShowScreen(
|
|
|
)
|
|
)
|
|
|
ModalBottomSheet(
|
|
ModalBottomSheet(
|
|
|
sheetState = sheetState,
|
|
sheetState = sheetState,
|
|
|
|
|
+ dragHandle = null,
|
|
|
onDismissRequest = { showKindSheet = false }
|
|
onDismissRequest = { showKindSheet = false }
|
|
|
) {
|
|
) {
|
|
|
Column(
|
|
Column(
|
|
|
modifier = Modifier
|
|
modifier = Modifier
|
|
|
|
|
+ .hazeEffect(state = hazeState, style = HazeMaterials.regular())
|
|
|
.fillMaxWidth()
|
|
.fillMaxWidth()
|
|
|
- .heightIn(max = LocalConfiguration.current.screenHeightDp.dp * 0.8f)
|
|
|
|
|
|
|
+ .heightIn(max = LocalConfiguration.current.screenHeightDp.dp * 0.72f)
|
|
|
.verticalScroll(scrollState)
|
|
.verticalScroll(scrollState)
|
|
|
) {
|
|
) {
|
|
|
|
|
|
|
@@ -255,7 +265,9 @@ fun ExploreShowScreen(
|
|
|
FlowRow(
|
|
FlowRow(
|
|
|
modifier = Modifier
|
|
modifier = Modifier
|
|
|
.fillMaxWidth()
|
|
.fillMaxWidth()
|
|
|
- .padding(16.dp)
|
|
|
|
|
|
|
+ .padding(16.dp),
|
|
|
|
|
+ maxItemsInEachRow = 3, // 强制每行最多 3 个
|
|
|
|
|
+ horizontalArrangement = Arrangement.spacedBy(0.dp) // 因为 Item 内部有 padding,这里设为 0
|
|
|
) {
|
|
) {
|
|
|
filteredKinds.forEach { kind ->
|
|
filteredKinds.forEach { kind ->
|
|
|
KindListItem(
|
|
KindListItem(
|
|
@@ -273,9 +285,15 @@ fun ExploreShowScreen(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Scaffold(
|
|
Scaffold(
|
|
|
- modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
|
|
|
|
|
|
+ modifier = Modifier
|
|
|
|
|
+ .nestedScroll(scrollBehavior.nestedScrollConnection)
|
|
|
|
|
+ .hazeSource(hazeState),
|
|
|
topBar = {
|
|
topBar = {
|
|
|
ExploreTopBar(
|
|
ExploreTopBar(
|
|
|
|
|
+ modifier = Modifier.hazeEffect(hazeState) {
|
|
|
|
|
+ progressive =
|
|
|
|
|
+ HazeProgressive.verticalGradient(startIntensity = 1f, endIntensity = 0f)
|
|
|
|
|
+ },
|
|
|
title = selectedTitle ?: title,
|
|
title = selectedTitle ?: title,
|
|
|
filterState = filterState,
|
|
filterState = filterState,
|
|
|
onBack = onBack,
|
|
onBack = onBack,
|
|
@@ -293,7 +311,6 @@ fun ExploreShowScreen(
|
|
|
.fillMaxSize()
|
|
.fillMaxSize()
|
|
|
.padding(paddingValues)
|
|
.padding(paddingValues)
|
|
|
) {
|
|
) {
|
|
|
-
|
|
|
|
|
Crossfade(
|
|
Crossfade(
|
|
|
targetState = isGridMode,
|
|
targetState = isGridMode,
|
|
|
animationSpec = tween(250),
|
|
animationSpec = tween(250),
|
|
@@ -338,7 +355,6 @@ fun ExploreShowScreen(
|
|
|
items = books,
|
|
items = books,
|
|
|
key = { it.bookUrl }
|
|
key = { it.bookUrl }
|
|
|
) { book ->
|
|
) { book ->
|
|
|
- val shelfState = viewModel.getCurrentBookShelfState(book)
|
|
|
|
|
ExploreBookItem(
|
|
ExploreBookItem(
|
|
|
book = book,
|
|
book = book,
|
|
|
shelfState = viewModel.getBookShelfStateFlow(book),
|
|
shelfState = viewModel.getBookShelfStateFlow(book),
|
|
@@ -364,13 +380,15 @@ fun ExploreShowScreen(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
|
@Composable
|
|
|
-fun KindListItem(
|
|
|
|
|
|
|
+fun FlowRowScope.KindListItem(
|
|
|
kind: ExploreKind,
|
|
kind: ExploreKind,
|
|
|
currentTitle: String?,
|
|
currentTitle: String?,
|
|
|
- onClick: () -> Unit
|
|
|
|
|
|
|
+ onClick: () -> Unit,
|
|
|
|
|
+ modifier: Modifier = Modifier
|
|
|
) {
|
|
) {
|
|
|
val isClickable = !kind.url.isNullOrBlank()
|
|
val isClickable = !kind.url.isNullOrBlank()
|
|
|
val isSelected = kind.title == currentTitle
|
|
val isSelected = kind.title == currentTitle
|
|
|
|
|
+
|
|
|
FilterChip(
|
|
FilterChip(
|
|
|
onClick = { if (isClickable) onClick() },
|
|
onClick = { if (isClickable) onClick() },
|
|
|
enabled = isClickable,
|
|
enabled = isClickable,
|
|
@@ -385,18 +403,19 @@ fun KindListItem(
|
|
|
overflow = TextOverflow.Ellipsis
|
|
overflow = TextOverflow.Ellipsis
|
|
|
)
|
|
)
|
|
|
},
|
|
},
|
|
|
- modifier = Modifier
|
|
|
|
|
|
|
+ modifier = modifier
|
|
|
|
|
+ .padding(horizontal = 4.dp)
|
|
|
.then(
|
|
.then(
|
|
|
if (!isClickable) Modifier.fillMaxWidth()
|
|
if (!isClickable) Modifier.fillMaxWidth()
|
|
|
- else Modifier.fillMaxWidth(1f / 3f)
|
|
|
|
|
|
|
+ else Modifier.weight(1f)
|
|
|
)
|
|
)
|
|
|
- .padding(horizontal = 4.dp)
|
|
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
|
|
@Composable
|
|
@Composable
|
|
|
fun ExploreTopBar(
|
|
fun ExploreTopBar(
|
|
|
|
|
+ modifier: Modifier = Modifier,
|
|
|
title: String,
|
|
title: String,
|
|
|
filterState: BookFilterState,
|
|
filterState: BookFilterState,
|
|
|
onBack: () -> Unit,
|
|
onBack: () -> Unit,
|
|
@@ -410,6 +429,7 @@ fun ExploreTopBar(
|
|
|
var showMenu by remember { mutableStateOf(false) }
|
|
var showMenu by remember { mutableStateOf(false) }
|
|
|
|
|
|
|
|
MediumFlexibleTopAppBar(
|
|
MediumFlexibleTopAppBar(
|
|
|
|
|
+ modifier = modifier,
|
|
|
title = { AnimatedTextLine(title, maxLines = 1, overflow = TextOverflow.Ellipsis) },
|
|
title = { AnimatedTextLine(title, maxLines = 1, overflow = TextOverflow.Ellipsis) },
|
|
|
navigationIcon = {
|
|
navigationIcon = {
|
|
|
IconButton(onClick = onBack) {
|
|
IconButton(onClick = onBack) {
|