2025-01-12 21:42:08 +08:00

249 lines
8.7 KiB
Kotlin

package com.taskttl.ui.screen
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.google.gson.Gson
import com.taskttl.R
import com.taskttl.data.composableWithCompositionLocal
import com.taskttl.data.ext.cancelRipperClick
import com.taskttl.data.local.entity.TaskEntity
import com.taskttl.ui.components.NotTask
import com.taskttl.ui.components.TaskItem
import com.taskttl.ui.viewmodel.IndexViewModel
import com.taskttl.utils.DateUtil
import com.taskttl.utils.Logger
/** 首页 */
const val INDEX_ROUTE = "index"
const val HOME_DETAIL_ROUTE = "home_detail"
fun NavController.navigateToIndex(navOptions: NavOptions) = navigate(INDEX_ROUTE, navOptions)
fun NavGraphBuilder.indexScreen(
snackBarHostState: SnackbarHostState,
// onSettingClick: () -> Unit,
// navigateToDetail: (ContentBean) -> Unit
) {
composable(INDEX_ROUTE) {
IndexScreen(
snackBarHostState,
// onSettingClick,
// navigateToDetail
)
}
}
fun NavGraphBuilder.homeDetailScreen(
navigateToHome: () -> Unit
) {
composableWithCompositionLocal(
route = "$HOME_DETAIL_ROUTE/{content}",
arguments = listOf(navArgument("content") { NavType.StringType })
) {
val contentBean = try {
Gson().fromJson(it.arguments?.getString("content") ?: "", TaskEntity::class.java)
} catch (e: Exception) {
null
}
HomeDetailRoute()
}
}
@Composable
fun HomeDetailRoute() {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(text = "Message")
}
}
/**
* 首页
* @param [navController] NAV 控制器
* @param [indexViewModel] 索引视图模型
*/
@Composable
fun IndexScreen(snackBarHostState: SnackbarHostState) {
val indexViewModel = hiltViewModel<IndexViewModel>();
val state by indexViewModel.uiState.collectAsState()
var isButtonVisible by remember { mutableStateOf(true) }
val listState = rememberLazyListState()
var backgroundHeight by remember { mutableStateOf(160.dp) }
val textTopPadding = remember(backgroundHeight) {
mutableStateOf(calculateTextTopPadding(backgroundHeight))
}
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.y
var dp = backgroundHeight + delta.dp
if (dp >= 160.dp) {
dp = 160.dp
}
backgroundHeight = maxOf(96.dp, dp) // 最小高度 100.dp
textTopPadding.value = calculateTextTopPadding(backgroundHeight)
return Offset.Zero
}
}
}
// 根据滚动状态来更新按钮的可见性
LaunchedEffect(remember { derivedStateOf { listState.firstVisibleItemIndex } }) {
isButtonVisible = listState.firstVisibleItemIndex == 0
}
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF1F5F9))
) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(backgroundHeight),
shape = RoundedCornerShape(0.dp)
) {
Box() {
// 背景图
Image(
painter = painterResource(R.mipmap.header),
modifier = Modifier
.fillMaxWidth()
.height(backgroundHeight),
contentScale = ContentScale.Crop,
contentDescription = "Header"
)
if (textTopPadding.value > 50.dp) {
Text(
DateUtil.today(),
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = 36.dp)
)
}
Text(
stringResource(R.string.index_title),
fontSize = 30.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = textTopPadding.value)
)
}
}
if (state.uncompletedList.isEmpty() && state.completedList.isEmpty()) {
NotTask()
} else {
Card(
modifier = Modifier
// .align(Alignment.TopCenter)
.padding(all = 10.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
shape = RoundedCornerShape(16.dp)
) {
LazyColumn(
state = listState,
modifier = Modifier
.fillMaxSize()
.nestedScroll(nestedScrollConnection)
) {
items(state.uncompletedList) {
TaskItem(it)
if (state.uncompletedList.indexOf(it) != state.uncompletedList.lastIndex) {
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
)
}
}
}
}
}
if (isButtonVisible) {
Row(
modifier = Modifier
// .align(Alignment.BottomCenter)
.padding(start = 16.dp, end = 16.dp, bottom = 24.dp)
.fillMaxWidth()
.height(56.dp)
.cancelRipperClick {
// navigateToDetail(contentBean)
// navController.navigate(RouteConfig.ADD)
},
// shape = RoundedCornerShape(50.dp)
) {
Text(text = stringResource(R.string.add_new_task))
}
}
}
}
// 计算 Text 组件的顶部偏移
fun calculateTextTopPadding(currentBackgroundHeight: Dp): Dp {
// 这里可以根据 backgroundHeight 的不同值返回不同的顶部偏移量
// 例如,保持文本始终位于背景图像的中心或某个固定位置。
return when (currentBackgroundHeight.value) {
in 96.0..160.0 -> (96 + (currentBackgroundHeight.value - 160)).dp
else -> 96.dp // 默认偏移
}
}