refactor: 删除 P0 冗余代码(自定义combine/死StateFlow/网格重复算法/runBlocking)

- 删除自定义 6 参数 combine + Quintuple,改用标准库嵌套 combine + Triple
- 删除 yearViewProgress 死 StateFlow 及关联测试
- generateMonthDays/generateMiniMonthDays 复用 getMonthGridInfo
- WeekPager 移除 runBlocking,交由 DayCell 自行异步计算
- 修复 AnimLog 在 JVM 单元测试中因 android.util.Log 不可用而崩溃
This commit is contained in:
xfy 2026-06-01 18:03:27 +08:00
parent e249700ee5
commit 829f89eb7c
6 changed files with 21 additions and 86 deletions

View File

@ -56,37 +56,6 @@ data class CalendarUiState(
val showLegalHoliday: Boolean
)
/**
* 将六个 [Flow] 合并为一个 [Flow]使用 [transform] 处理最新值
*
* kotlinx-coroutines 1.11 仅内置到 5 参数的 [combine] 重载
* 此扩展用于扁平化 6 StateFlow 的合并避免多层嵌套产生的中间流
*/
private inline fun <T1, T2, T3, T4, T5, T6, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
flow3: Flow<T3>,
flow4: Flow<T4>,
flow5: Flow<T5>,
flow6: Flow<T6>,
crossinline transform: suspend (T1, T2, T3, T4, T5, T6) -> R
): Flow<R> = combine(
combine(flow, flow2, flow3, flow4, flow5) { t1, t2, t3, t4, t5 ->
Quintuple(t1, t2, t3, t4, t5)
},
flow6
) { quintuple, t6 ->
transform(quintuple.first, quintuple.second, quintuple.third, quintuple.fourth, quintuple.fifth, t6)
}
private data class Quintuple<T1, T2, T3, T4, T5>(
val first: T1,
val second: T2,
val third: T3,
val fourth: T4,
val fifth: T5
)
/**
* 日历状态管理持有选中日期折叠状态和 ISO 周号计算逻辑
*
@ -141,9 +110,6 @@ class CalendarViewModel(
private val _isYearView = MutableStateFlow(false)
val isYearView: StateFlow<Boolean> = _isYearView.asStateFlow()
private val _yearViewProgress = MutableStateFlow(0f)
val yearViewProgress: StateFlow<Float> = _yearViewProgress.asStateFlow()
private val _yearViewYear = MutableStateFlow(today.year)
val yearViewYear: StateFlow<Int> = _yearViewYear.asStateFlow()
@ -170,13 +136,9 @@ class CalendarViewModel(
/** 聚合 UI 状态,减少 Compose 层分散订阅导致的重组。 */
val uiState: StateFlow<CalendarUiState> = combine(
_selectedDate,
_isCollapsed,
_isYearView,
_yearViewYear,
_collapseProgress,
_showLegalHoliday
) { selectedDate, isCollapsed, isYearView, yearViewYear, collapseProgress, showLegalHoliday ->
combine(_selectedDate, _isCollapsed, _isYearView) { s, c, y -> Triple(s, c, y) },
combine(_yearViewYear, _collapseProgress, _showLegalHoliday) { y, p, h -> Triple(y, p, h) }
) { (selectedDate, isCollapsed, isYearView), (yearViewYear, collapseProgress, showLegalHoliday) ->
CalendarUiState(
selectedDate = selectedDate,
isCollapsed = isCollapsed,
@ -212,8 +174,6 @@ class CalendarViewModel(
if (_isYearView.value) {
logd(TAG_VM, "[toggleYearView] ===== START Year→Month t=$t0 =====")
composeTraceBeginSection("YearView→MonthView")
_yearViewProgress.value = 0f
logd(TAG_VM, "[toggleYearView] yearViewProgress=0 dt=${(System.nanoTime() - t0) / 1_000_000}ms")
_isYearView.value = false
logd(TAG_VM, "[toggleYearView] isYearView=false dt=${(System.nanoTime() - t0) / 1_000_000}ms")
composeTraceEndSection()
@ -223,8 +183,6 @@ class CalendarViewModel(
composeTraceBeginSection("MonthView→YearView")
_yearViewYear.value = _selectedDate.value.year
logd(TAG_VM, "[toggleYearView] yearViewYear=${_yearViewYear.value} dt=${(System.nanoTime() - t0) / 1_000_000}ms")
_yearViewProgress.value = 1f
logd(TAG_VM, "[toggleYearView] yearViewProgress=1 dt=${(System.nanoTime() - t0) / 1_000_000}ms")
_isYearView.value = true
logd(TAG_VM, "[toggleYearView] isYearView=true dt=${(System.nanoTime() - t0) / 1_000_000}ms")
composeTraceEndSection()
@ -253,8 +211,6 @@ class CalendarViewModel(
logd(TAG_VM, "[selectMonthFromYearView] selectedDate set dt=${(System.nanoTime() - t0) / 1_000_000}ms")
_isYearView.value = false
logd(TAG_VM, "[selectMonthFromYearView] isYearView=false dt=${(System.nanoTime() - t0) / 1_000_000}ms")
_yearViewProgress.value = 0f
logd(TAG_VM, "[selectMonthFromYearView] yearViewProgress=0 dt=${(System.nanoTime() - t0) / 1_000_000}ms")
composeTraceEndSection()
logd(TAG_VM, "[selectMonthFromYearView] ===== END total=${(System.nanoTime() - t0) / 1_000_000}ms =====")
}

View File

@ -292,16 +292,9 @@ data class HolidayEdgeInfo(
)
private fun generateMonthDays(year: Int, month: Int): List<DayData> {
val firstOfMonth = LocalDate(year, Month(month), 1)
val offset = firstOfMonth.dayOfWeek.ordinal
val startDate = firstOfMonth.minus(DatePeriod(days = offset))
val nextMonth = if (month == 12) LocalDate(year + 1, 1, 1) else LocalDate(year, Month(month + 1), 1)
val daysInMonth = nextMonth.minus(DatePeriod(days = 1)).day
val rows = ((offset + daysInMonth - 1) / 7) + 1
val totalDays = rows * 7
return (0 until totalDays).map { i ->
val date = startDate.plus(DatePeriod(days = i))
val info = getMonthGridInfo(year, month)
return (0 until info.totalDays).map { i ->
val date = info.startDate.plus(DatePeriod(days = i))
DayData(
date = date,
isCurrentMonth = date.month.number == month && date.year == year

View File

@ -18,13 +18,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate
import kotlinx.datetime.daysUntil
import kotlinx.datetime.plus
import plus.rua.project.DayCellInfo
import plus.rua.project.LunarCache
import plus.rua.project.ShiftKind
import plus.rua.project.composeTraceBeginSection
import plus.rua.project.composeTraceEndSection
@ -99,11 +96,6 @@ fun WeekPager(
) {
(0 until 7).forEach { dayOffset ->
val date = weekMonday.plus(DatePeriod(days = dayOffset))
val lunarData = remember(date) {
runBlocking {
LunarCache.default.getOrCompute(date)
}
}
DayCell(
date = date,
isCurrentMonth = date.month == selectedDate.month
@ -115,8 +107,7 @@ fun WeekPager(
cellIndex = dayOffset,
onClick = { onDateClick(date) },
modifier = Modifier.weight(1f),
interactionSource = interactionSource,
lunarData = lunarData
interactionSource = interactionSource
)
}
}

View File

@ -307,16 +307,9 @@ private data class MiniDayData(
private fun generateMiniMonthDays(year: Int, month: Int): List<MiniDayData> {
composeTraceBeginSection("generateMiniMonthDays:$year-$month")
val firstOfMonth = LocalDate(year, Month(month), 1)
val offset = firstOfMonth.dayOfWeek.ordinal
val startDate = firstOfMonth.minus(DatePeriod(days = offset))
val nextMonth = if (month == 12) LocalDate(year + 1, 1, 1) else LocalDate(year, Month(month + 1), 1)
val daysInMonth = nextMonth.minus(DatePeriod(days = 1)).day
val rows = ((offset + daysInMonth - 1) / 7) + 1
val totalDays = rows * 7
val result = (0 until totalDays).map { i ->
val date = startDate.plus(DatePeriod(days = i))
val info = getMonthGridInfo(year, month)
val result = (0 until info.totalDays).map { i ->
val date = info.startDate.plus(DatePeriod(days = i))
MiniDayData(
date = date,
isCurrentMonth = date.month.number == month && date.year == year

View File

@ -6,13 +6,22 @@ import plus.rua.project.shared.BuildConfig
@Suppress("NOTHING_TO_INLINE")
inline fun logd(tag: String, message: () -> String) {
if (BuildConfig.DEBUG) {
Log.d(tag, message())
try {
Log.d(tag, message())
} catch (_: RuntimeException) {
// Android Log not available in JVM unit tests; fallback to stdout
println("D/$tag: ${message()}")
}
}
}
@Suppress("NOTHING_TO_INLINE")
inline fun logd(tag: String, message: String) {
if (BuildConfig.DEBUG) {
Log.d(tag, message)
try {
Log.d(tag, message)
} catch (_: RuntimeException) {
println("D/$tag: $message")
}
}
}

View File

@ -52,11 +52,6 @@ class CalendarViewModelStateTest {
assertFalse(createViewModel().isYearView.value)
}
@Test
fun init_yearViewProgressDefaultsZero() {
assertEquals(0f, createViewModel().yearViewProgress.value, 0.001f)
}
@Test
fun init_yearViewYearDefaultsToTodayYear() {
assertEquals(2026, createViewModel().yearViewYear.value)
@ -375,7 +370,6 @@ class CalendarViewModelStateTest {
assertFalse(vm.isYearView.value)
vm.toggleYearView()
assertTrue(vm.isYearView.value)
assertEquals(1f, vm.yearViewProgress.value, 0.001f)
}
@Test
@ -385,7 +379,6 @@ class CalendarViewModelStateTest {
assertTrue(vm.isYearView.value)
vm.toggleYearView()
assertFalse(vm.isYearView.value)
assertEquals(0f, vm.yearViewProgress.value, 0.001f)
}
@Test