|
@@ -1,18 +1,245 @@
|
|
|
package com.lostc.japp.ui.features.challenge
|
|
package com.lostc.japp.ui.features.challenge
|
|
|
|
|
|
|
|
|
|
+import androidx.compose.foundation.background
|
|
|
|
|
+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.PaddingValues
|
|
|
|
|
+import androidx.compose.foundation.layout.Row
|
|
|
|
|
+import androidx.compose.foundation.layout.Spacer
|
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
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.lazy.LazyColumn
|
|
|
|
|
+import androidx.compose.foundation.lazy.items
|
|
|
|
|
+import androidx.compose.foundation.shape.CircleShape
|
|
|
|
|
+import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
|
|
+import androidx.compose.material.icons.Icons
|
|
|
|
|
+import androidx.compose.material.icons.automirrored.filled.List
|
|
|
|
|
+import androidx.compose.material.icons.filled.AccessTime
|
|
|
|
|
+import androidx.compose.material.icons.filled.PlayArrow
|
|
|
|
|
+import androidx.compose.material.icons.filled.Refresh
|
|
|
|
|
+import androidx.compose.material3.Button
|
|
|
|
|
+import androidx.compose.material3.Card
|
|
|
|
|
+import androidx.compose.material3.CardDefaults
|
|
|
|
|
+import androidx.compose.material3.CircularProgressIndicator
|
|
|
|
|
+import androidx.compose.material3.ExperimentalMaterial3Api
|
|
|
|
|
+import androidx.compose.material3.Icon
|
|
|
|
|
+import androidx.compose.material3.IconButton
|
|
|
|
|
+import androidx.compose.material3.MaterialTheme
|
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.material3.Text
|
|
|
|
|
+import androidx.compose.material3.TopAppBar
|
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.Composable
|
|
|
|
|
+import androidx.compose.runtime.getValue
|
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Alignment
|
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.Modifier
|
|
|
|
|
+import androidx.compose.ui.graphics.Color
|
|
|
|
|
+import androidx.compose.ui.platform.LocalContext
|
|
|
|
|
+import androidx.compose.ui.text.font.FontWeight
|
|
|
|
|
+import androidx.compose.ui.text.style.TextOverflow
|
|
|
|
|
+import androidx.compose.ui.unit.dp
|
|
|
|
|
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
|
|
|
+import androidx.lifecycle.viewmodel.compose.viewModel
|
|
|
|
|
+import com.lostc.japp.MyApplication
|
|
|
|
|
+import com.lostc.japp.data.model.Challenge
|
|
|
|
|
+import com.lostc.japp.data.model.Difficulty
|
|
|
|
|
+
|
|
|
|
|
+//@Composable
|
|
|
|
|
+//fun ChallengeListScreen(modifier: Modifier = Modifier) {
|
|
|
|
|
+// Box(
|
|
|
|
|
+// modifier = Modifier.fillMaxSize(),
|
|
|
|
|
+// contentAlignment = Alignment.Center
|
|
|
|
|
+// ) {
|
|
|
|
|
+// Text("Challenge Screen")
|
|
|
|
|
+// }
|
|
|
|
|
+//}
|
|
|
|
|
+
|
|
|
|
|
+// ChallengeListScreen.kt
|
|
|
|
|
+@OptIn(ExperimentalMaterial3Api::class)
|
|
|
|
|
+@Composable
|
|
|
|
|
+fun ChallengeListScreen(
|
|
|
|
|
+ viewModel: ChallengeViewModel = viewModel(
|
|
|
|
|
+ factory = ChallengeViewModelFactory((LocalContext.current.applicationContext as MyApplication).repository)
|
|
|
|
|
+ )
|
|
|
|
|
+) {
|
|
|
|
|
+ val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
|
|
|
|
+
|
|
|
|
|
+ Column(modifier = Modifier.fillMaxSize()) {
|
|
|
|
|
+ TopAppBar(
|
|
|
|
|
+ title = { Text("挑战列表") },
|
|
|
|
|
+ actions = {
|
|
|
|
|
+ IconButton(onClick = { viewModel.refreshChallenges() }) {
|
|
|
|
|
+ Icon(Icons.Filled.Refresh, contentDescription = "刷新")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ when {
|
|
|
|
|
+ uiState.isLoading -> {
|
|
|
|
|
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
|
|
|
|
+ CircularProgressIndicator()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ uiState.error != null -> {
|
|
|
|
|
+ ErrorView(message = uiState.error!!) {
|
|
|
|
|
+ viewModel.refreshChallenges()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ uiState.challenges.isEmpty() -> {
|
|
|
|
|
+ EmptyView()
|
|
|
|
|
+ }
|
|
|
|
|
+ else -> {
|
|
|
|
|
+ ChallengeList(challenges = uiState.challenges)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@Composable
|
|
|
|
|
+fun ChallengeList(challenges: List<Challenge>) {
|
|
|
|
|
+ LazyColumn(
|
|
|
|
|
+ modifier = Modifier.fillMaxSize(),
|
|
|
|
|
+ contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
|
|
|
|
|
+ ) {
|
|
|
|
|
+ items(challenges) { challenge ->
|
|
|
|
|
+ ChallengeCard(challenge = challenge)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@Composable
|
|
|
|
|
+fun ChallengeCard(challenge: Challenge) {
|
|
|
|
|
+ Card(
|
|
|
|
|
+ modifier = Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .padding(vertical = 8.dp),
|
|
|
|
|
+ elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Column(
|
|
|
|
|
+ modifier = Modifier.padding(16.dp)
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Row(
|
|
|
|
|
+ modifier = Modifier.fillMaxWidth(),
|
|
|
|
|
+ horizontalArrangement = Arrangement.SpaceBetween
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Text(
|
|
|
|
|
+ text = challenge.title,
|
|
|
|
|
+ style = MaterialTheme.typography.titleMedium,
|
|
|
|
|
+ fontWeight = FontWeight.Bold
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ // 难度标签
|
|
|
|
|
+ Box(
|
|
|
|
|
+ contentAlignment = Alignment.Center,
|
|
|
|
|
+ modifier = Modifier
|
|
|
|
|
+ .padding(start = 8.dp)
|
|
|
|
|
+ .background(
|
|
|
|
|
+ color = when (challenge.difficulty) {
|
|
|
|
|
+ Difficulty.EASY -> Color(0xFFA5D6A7)
|
|
|
|
|
+ Difficulty.MEDIUM -> Color(0xFFFFECB3)
|
|
|
|
|
+ Difficulty.HARD -> Color(0xFFF5DEB3)
|
|
|
|
|
+ Difficulty.HELL -> Color(0xFFF5DEB3)
|
|
|
|
|
+ },
|
|
|
|
|
+ shape = RoundedCornerShape(12.dp)
|
|
|
|
|
+ )
|
|
|
|
|
+ .padding(horizontal = 8.dp, vertical = 4.dp)
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Text(
|
|
|
|
|
+ text = challenge.difficulty.name,
|
|
|
|
|
+ style = MaterialTheme.typography.labelSmall,
|
|
|
|
|
+ color = when (challenge.difficulty) {
|
|
|
|
|
+ Difficulty.EASY -> Color(0xFF2E7D32)
|
|
|
|
|
+ Difficulty.MEDIUM -> Color(0xFFFF8F00)
|
|
|
|
|
+ Difficulty.HARD -> Color(0xFFBF360C)
|
|
|
|
|
+ Difficulty.HELL->Color(0xFFBF360C)
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Spacer(modifier = Modifier.height(8.dp))
|
|
|
|
|
+
|
|
|
|
|
+ Text(
|
|
|
|
|
+ text = challenge.description,
|
|
|
|
|
+ style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
|
|
|
+ maxLines = 2,
|
|
|
|
|
+ overflow = TextOverflow.Ellipsis
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ Spacer(modifier = Modifier.height(12.dp))
|
|
|
|
|
+
|
|
|
|
|
+ Row(
|
|
|
|
|
+ modifier = Modifier.fillMaxWidth(),
|
|
|
|
|
+ horizontalArrangement = Arrangement.SpaceBetween
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Row(verticalAlignment = Alignment.CenterVertically) {
|
|
|
|
|
+ Icon(
|
|
|
|
|
+ imageVector = Icons.Filled.AccessTime,
|
|
|
|
|
+ contentDescription = "时长",
|
|
|
|
|
+ modifier = Modifier.size(16.dp)
|
|
|
|
|
+ )
|
|
|
|
|
+ Text(
|
|
|
|
|
+ text = "${challenge.actionDays} 天",
|
|
|
|
|
+ style = MaterialTheme.typography.labelSmall,
|
|
|
|
|
+ modifier = Modifier.padding(start = 4.dp)
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 进度指示器或其他操作按钮
|
|
|
|
|
+ Button(
|
|
|
|
|
+ onClick = { /* 处理挑战项点击 */ },
|
|
|
|
|
+ modifier = Modifier.size(40.dp, 40.dp),
|
|
|
|
|
+ shape = CircleShape
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Icon(Icons.Filled.PlayArrow, contentDescription = "开始挑战")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@Composable
|
|
|
|
|
+fun ErrorView(message: String, onRetry: () -> Unit) {
|
|
|
|
|
+ Column(
|
|
|
|
|
+ modifier = Modifier.fillMaxSize(),
|
|
|
|
|
+ verticalArrangement = Arrangement.Center,
|
|
|
|
|
+ horizontalAlignment = Alignment.CenterHorizontally
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Text(
|
|
|
|
|
+ text = "出错了",
|
|
|
|
|
+ style = MaterialTheme.typography.headlineMedium,
|
|
|
|
|
+ color = MaterialTheme.colorScheme.error
|
|
|
|
|
+ )
|
|
|
|
|
+ Spacer(modifier = Modifier.height(16.dp))
|
|
|
|
|
+ Text(text = message)
|
|
|
|
|
+ Spacer(modifier = Modifier.height(24.dp))
|
|
|
|
|
+ Button(onClick = onRetry) {
|
|
|
|
|
+ Text("重试")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
@Composable
|
|
@Composable
|
|
|
-fun ChallengeListScreen(modifier: Modifier = Modifier) {
|
|
|
|
|
- Box(
|
|
|
|
|
|
|
+fun EmptyView() {
|
|
|
|
|
+ Column(
|
|
|
modifier = Modifier.fillMaxSize(),
|
|
modifier = Modifier.fillMaxSize(),
|
|
|
- contentAlignment = Alignment.Center
|
|
|
|
|
|
|
+ verticalArrangement = Arrangement.Center,
|
|
|
|
|
+ horizontalAlignment = Alignment.CenterHorizontally
|
|
|
) {
|
|
) {
|
|
|
- Text("Challenge Screen")
|
|
|
|
|
|
|
+ Icon(
|
|
|
|
|
+ imageVector = Icons.AutoMirrored.Filled.List,
|
|
|
|
|
+ contentDescription = "空列表",
|
|
|
|
|
+ modifier = Modifier.size(64.dp),
|
|
|
|
|
+ tint = MaterialTheme.colorScheme.outline
|
|
|
|
|
+ )
|
|
|
|
|
+ Spacer(modifier = Modifier.height(16.dp))
|
|
|
|
|
+ Text(
|
|
|
|
|
+ text = "暂无挑战",
|
|
|
|
|
+ style = MaterialTheme.typography.headlineMedium,
|
|
|
|
|
+ color = MaterialTheme.colorScheme.outline
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|