v1.1.1 我的页面增加节日倒计时

This commit is contained in:
amos
2025-12-18 14:11:23 +08:00
parent f58df76147
commit 896bbbfe45
3 changed files with 248 additions and 2 deletions

View File

@@ -13,8 +13,8 @@ android {
applicationId = "com.memory.app"
minSdk = 26
targetSdk = 35
versionCode = 2
versionName = "1.1.0"
versionCode = 3
versionName = "1.1.1"
buildConfigField("String", "API_BASE_URL", "\"https://x.amos.us.kg/api/\"")
}

View File

@@ -3,10 +3,13 @@ package com.memory.app.ui.screen
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
@@ -19,7 +22,9 @@ import androidx.compose.runtime.*
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.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
@@ -27,7 +32,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import com.memory.app.data.model.User
import com.memory.app.ui.theme.*
import com.memory.app.util.HolidayUtils
import com.memory.app.util.HolidayWithDays
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -60,6 +68,7 @@ fun ProfileScreen(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.verticalScroll(rememberScrollState())
) {
// Header with theme toggle and logout button
Row(
@@ -261,10 +270,146 @@ fun ProfileScreen(
// Divider
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant, thickness = 1.dp)
Spacer(modifier = Modifier.height(24.dp))
// Holiday Countdown
HolidayCountdown()
}
}
}
@Composable
private fun HolidayCountdown() {
var showAll by remember { mutableStateOf(false) }
val allHolidays = remember { HolidayUtils.getUpcomingHolidays(20) }
if (allHolidays.isEmpty()) return
val mainHoliday = allHolidays.first()
val upcomingHolidays = if (showAll) allHolidays.drop(1) else allHolidays.drop(1).take(2)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
// Main Holiday Card
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(20.dp))
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, RoundedCornerShape(20.dp))
.padding(vertical = 24.dp, horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = mainHoliday.holiday.emoji,
fontSize = 48.sp
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = mainHoliday.holiday.name,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onBackground
)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = mainHoliday.getDaysText(),
fontSize = 36.sp,
fontWeight = FontWeight.Bold,
color = Brand500
)
Spacer(modifier = Modifier.height(4.dp))
val dateText = buildString {
append(mainHoliday.getDateText())
mainHoliday.getWeekdayText()?.let { append(" $it") }
}
Text(
text = dateText,
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
// Upcoming Holidays
if (upcomingHolidays.isNotEmpty()) {
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "接下来",
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = if (showAll) "收起" else "更多",
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = Brand500,
modifier = Modifier.clickable { showAll = !showAll }
)
}
Spacer(modifier = Modifier.height(12.dp))
upcomingHolidays.forEach { holidayWithDays ->
UpcomingHolidayItem(holidayWithDays)
Spacer(modifier = Modifier.height(8.dp))
}
}
}
}
@Composable
private fun UpcomingHolidayItem(holidayWithDays: HolidayWithDays) {
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f))
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Emoji
Text(
text = holidayWithDays.holiday.emoji,
fontSize = 24.sp
)
Spacer(modifier = Modifier.width(12.dp))
// Name
Text(
text = holidayWithDays.holiday.name,
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.weight(1f)
)
// Days
Text(
text = holidayWithDays.getDaysText(),
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
color = Brand500
)
}
}
@Composable
private fun StatColumn(count: Int, label: String) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
@@ -290,3 +435,5 @@ private fun StatColumn(count: Int, label: String) {
)
}
}

View File

@@ -0,0 +1,99 @@
package com.memory.app.util
import java.time.LocalDate
import java.time.temporal.ChronoUnit
data class Holiday(
val name: String,
val emoji: String,
val date: LocalDate,
val isLunar: Boolean = false
)
object HolidayUtils {
// 获取所有节日2025-2026年
private fun getAllHolidays(): List<Holiday> {
return listOf(
// 2024年剩余
Holiday("圣诞节", "🎄", LocalDate.of(2024, 12, 25)),
// 2025年
Holiday("元旦", "🎊", LocalDate.of(2025, 1, 1)),
Holiday("春节", "🧧", LocalDate.of(2025, 1, 29), true),
Holiday("元宵节", "🏮", LocalDate.of(2025, 2, 12), true),
Holiday("情人节", "💕", LocalDate.of(2025, 2, 14)),
Holiday("清明节", "🌿", LocalDate.of(2025, 4, 4)),
Holiday("劳动节", "💪", LocalDate.of(2025, 5, 1)),
Holiday("母亲节", "🌸", LocalDate.of(2025, 5, 11)),
Holiday("端午节", "🐲", LocalDate.of(2025, 5, 31), true),
Holiday("父亲节", "👔", LocalDate.of(2025, 6, 15)),
Holiday("七夕节", "🎋", LocalDate.of(2025, 8, 29), true),
Holiday("中秋节", "🥮", LocalDate.of(2025, 10, 6), true),
Holiday("国庆节", "🇨🇳", LocalDate.of(2025, 10, 1)),
Holiday("重阳节", "🍂", LocalDate.of(2025, 10, 29), true),
Holiday("万圣节", "🎃", LocalDate.of(2025, 10, 31)),
Holiday("感恩节", "🦃", LocalDate.of(2025, 11, 27)),
Holiday("圣诞节", "🎄", LocalDate.of(2025, 12, 25)),
// 2026年
Holiday("元旦", "🎊", LocalDate.of(2026, 1, 1)),
Holiday("春节", "🧧", LocalDate.of(2026, 2, 17), true),
Holiday("元宵节", "🏮", LocalDate.of(2026, 3, 3), true),
Holiday("情人节", "💕", LocalDate.of(2026, 2, 14)),
Holiday("清明节", "🌿", LocalDate.of(2026, 4, 5)),
Holiday("劳动节", "💪", LocalDate.of(2026, 5, 1)),
Holiday("母亲节", "🌸", LocalDate.of(2026, 5, 10)),
Holiday("端午节", "🐲", LocalDate.of(2026, 6, 19), true),
Holiday("父亲节", "👔", LocalDate.of(2026, 6, 21)),
Holiday("七夕节", "🎋", LocalDate.of(2026, 8, 19), true),
Holiday("中秋节", "🥮", LocalDate.of(2026, 9, 25), true),
Holiday("国庆节", "🇨🇳", LocalDate.of(2026, 10, 1)),
Holiday("重阳节", "🍂", LocalDate.of(2026, 10, 18), true),
Holiday("万圣节", "🎃", LocalDate.of(2026, 10, 31)),
Holiday("感恩节", "🦃", LocalDate.of(2026, 11, 26)),
Holiday("圣诞节", "🎄", LocalDate.of(2026, 12, 25))
)
}
// 获取即将到来的节日(包括今天)
fun getUpcomingHolidays(count: Int = 3): List<HolidayWithDays> {
val today = LocalDate.now()
return getAllHolidays()
.filter { !it.date.isBefore(today) }
.sortedBy { it.date }
.take(count)
.map { holiday ->
val daysUntil = ChronoUnit.DAYS.between(today, holiday.date).toInt()
HolidayWithDays(holiday, daysUntil)
}
}
}
data class HolidayWithDays(
val holiday: Holiday,
val daysUntil: Int
) {
fun getDaysText(): String {
return when (daysUntil) {
0 -> "今天"
1 -> "明天"
else -> "${daysUntil}"
}
}
fun getDateText(): String {
val date = holiday.date
return "${date.monthValue}${date.dayOfMonth}"
}
// 如果在下周内2-7天返回周几否则返回null
fun getWeekdayText(): String? {
if (daysUntil in 2..7) {
val weekdays = listOf("周一", "周二", "周三", "周四", "周五", "周六", "周日")
val dayOfWeek = holiday.date.dayOfWeek.value // 1=Monday, 7=Sunday
return weekdays[dayOfWeek - 1]
}
return null
}
}