Files
TaskTTL/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownDetailScreen.kt
2025-10-15 10:15:55 +08:00

262 lines
10 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.taskttl.presentation.countdown
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CalendarToday
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.taskttl.core.ui.Chip
import com.taskttl.core.ui.LoadingOverlay
import com.taskttl.core.utils.DateUtils
import com.taskttl.data.state.CountdownEffect
import com.taskttl.data.viewmodel.CountdownViewModel
import com.taskttl.ui.components.AppHeader
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import taskttl.composeapp.generated.resources.Res
import taskttl.composeapp.generated.resources.countdown_not_found
import taskttl.composeapp.generated.resources.detail_information
import taskttl.composeapp.generated.resources.event_description
import taskttl.composeapp.generated.resources.label_created_at
import taskttl.composeapp.generated.resources.label_days
import taskttl.composeapp.generated.resources.reminder
import taskttl.composeapp.generated.resources.title_countdown_info
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CountdownDetailScreen(
countdownId: String,
onNavigateBack: () -> Unit,
onNavigateToEdit: () -> Unit,
viewModel: CountdownViewModel = koinViewModel()
) {
val state by viewModel.state.collectAsState()
val countdown = state.countdowns.find { it.id == countdownId }
LaunchedEffect(Unit) {
viewModel.effects.collect { effect ->
when (effect) {
is CountdownEffect.NavigateBack -> {
onNavigateBack()
}
else -> {}
}
}
}
if (countdown == null) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(stringResource(Res.string.countdown_not_found))
}
return
}
// 剩余天数
val daysRemaining = DateUtils.daysRemaining(countdown.targetDate)
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.White)
) {
Column(
modifier = Modifier.fillMaxSize()
) {
AppHeader(
title = Res.string.title_countdown_info,
showBack = true,
onBackClick = { onNavigateBack.invoke() },
trailingIcon = Icons.Default.Edit,
onTrailingClick = { onNavigateToEdit.invoke() }
)
Column(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.padding(16.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(20.dp))
.background(
brush = androidx.compose.ui.graphics.Brush.linearGradient(
colors = listOf(
countdown.category.color.backgroundColor,
Color.Transparent
)
)
)
.padding(20.dp)
) {
Column(modifier = Modifier.align(Alignment.CenterStart)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = countdown.title,
fontSize = 18.sp,
fontWeight = FontWeight.ExtraBold,
color = Color(0xFF111111)
)
Spacer(modifier = Modifier.width(6.dp))
Chip(text = countdown.category.name)
}
Spacer(modifier = Modifier.height(6.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Default.CalendarToday,
contentDescription = null
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = countdown.targetDate.toString(),
fontSize = 14.sp,
color = Color(0xFF444444)
)
}
}
Column(
modifier = Modifier.align(Alignment.TopEnd),
horizontalAlignment = Alignment.End
) {
Text(
text = daysRemaining.toString(),
fontSize = 44.sp,
fontWeight = FontWeight.ExtraBold,
color = Color(0xFF111111)
)
Text(
text = stringResource(Res.string.label_days),
fontSize = 12.sp,
fontWeight = FontWeight.SemiBold,
color = Color(0xFF666666)
)
}
}
countdown.description.let {
Spacer(modifier = Modifier.height(16.dp))
Column(modifier = Modifier.fillMaxWidth()) {
Text(
text = stringResource(Res.string.event_description),
fontWeight = FontWeight.SemiBold,
fontSize = 14.sp,
color = Color(0xFF333333)
)
Spacer(modifier = Modifier.height(10.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.background(Color.White)
.padding(12.dp)
) {
Column {
Text(
text = countdown.description,
color = countdown.category.color.textColor,
lineHeight = 20.sp
)
}
}
}
}
Spacer(modifier = Modifier.height(12.dp))
Column(modifier = Modifier.fillMaxWidth()) {
Text(
text = stringResource(Res.string.detail_information),
fontWeight = FontWeight.SemiBold,
fontSize = 14.sp,
color = Color(0xFF333333)
)
Spacer(modifier = Modifier.height(10.dp))
Column {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
InfoItem(
iconTint = Color(0xFF667EEA),
text = "${stringResource(Res.string.reminder)}${
stringResource(countdown.notificationEnabled.displayNameRes)
}",
modifier = Modifier.weight(1f)
)
}
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth()) {
InfoItem(
iconTint = Color(0xFF999999),
text = "${stringResource(Res.string.label_created_at)}${countdown.createdAt}",
modifier = Modifier.fillMaxWidth()
)
}
}
}
}
}
LoadingOverlay(state.isLoading)
}
}
@Composable
private fun InfoItem(iconTint: Color, text: String, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.clip(RoundedCornerShape(10.dp))
.background(Color.White)
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(18.dp)
.clip(RoundedCornerShape(6.dp))
.background(iconTint)
) {}
Spacer(modifier = Modifier.width(10.dp))
Text(text = text, fontSize = 13.sp, color = Color(0xFF555555))
}
}