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 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 周号计算逻辑 * 日历状态管理持有选中日期折叠状态和 ISO 周号计算逻辑
* *
@ -141,9 +110,6 @@ class CalendarViewModel(
private val _isYearView = MutableStateFlow(false) private val _isYearView = MutableStateFlow(false)
val isYearView: StateFlow<Boolean> = _isYearView.asStateFlow() val isYearView: StateFlow<Boolean> = _isYearView.asStateFlow()
private val _yearViewProgress = MutableStateFlow(0f)
val yearViewProgress: StateFlow<Float> = _yearViewProgress.asStateFlow()
private val _yearViewYear = MutableStateFlow(today.year) private val _yearViewYear = MutableStateFlow(today.year)
val yearViewYear: StateFlow<Int> = _yearViewYear.asStateFlow() val yearViewYear: StateFlow<Int> = _yearViewYear.asStateFlow()
@ -170,13 +136,9 @@ class CalendarViewModel(
/** 聚合 UI 状态,减少 Compose 层分散订阅导致的重组。 */ /** 聚合 UI 状态,减少 Compose 层分散订阅导致的重组。 */
val uiState: StateFlow<CalendarUiState> = combine( val uiState: StateFlow<CalendarUiState> = combine(
_selectedDate, combine(_selectedDate, _isCollapsed, _isYearView) { s, c, y -> Triple(s, c, y) },
_isCollapsed, combine(_yearViewYear, _collapseProgress, _showLegalHoliday) { y, p, h -> Triple(y, p, h) }
_isYearView, ) { (selectedDate, isCollapsed, isYearView), (yearViewYear, collapseProgress, showLegalHoliday) ->
_yearViewYear,
_collapseProgress,
_showLegalHoliday
) { selectedDate, isCollapsed, isYearView, yearViewYear, collapseProgress, showLegalHoliday ->
CalendarUiState( CalendarUiState(
selectedDate = selectedDate, selectedDate = selectedDate,
isCollapsed = isCollapsed, isCollapsed = isCollapsed,
@ -212,8 +174,6 @@ class CalendarViewModel(
if (_isYearView.value) { if (_isYearView.value) {
logd(TAG_VM, "[toggleYearView] ===== START Year→Month t=$t0 =====") logd(TAG_VM, "[toggleYearView] ===== START Year→Month t=$t0 =====")
composeTraceBeginSection("YearView→MonthView") composeTraceBeginSection("YearView→MonthView")
_yearViewProgress.value = 0f
logd(TAG_VM, "[toggleYearView] yearViewProgress=0 dt=${(System.nanoTime() - t0) / 1_000_000}ms")
_isYearView.value = false _isYearView.value = false
logd(TAG_VM, "[toggleYearView] isYearView=false dt=${(System.nanoTime() - t0) / 1_000_000}ms") logd(TAG_VM, "[toggleYearView] isYearView=false dt=${(System.nanoTime() - t0) / 1_000_000}ms")
composeTraceEndSection() composeTraceEndSection()
@ -223,8 +183,6 @@ class CalendarViewModel(
composeTraceBeginSection("MonthView→YearView") composeTraceBeginSection("MonthView→YearView")
_yearViewYear.value = _selectedDate.value.year _yearViewYear.value = _selectedDate.value.year
logd(TAG_VM, "[toggleYearView] yearViewYear=${_yearViewYear.value} dt=${(System.nanoTime() - t0) / 1_000_000}ms") 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 _isYearView.value = true
logd(TAG_VM, "[toggleYearView] isYearView=true dt=${(System.nanoTime() - t0) / 1_000_000}ms") logd(TAG_VM, "[toggleYearView] isYearView=true dt=${(System.nanoTime() - t0) / 1_000_000}ms")
composeTraceEndSection() composeTraceEndSection()
@ -253,8 +211,6 @@ class CalendarViewModel(
logd(TAG_VM, "[selectMonthFromYearView] selectedDate set dt=${(System.nanoTime() - t0) / 1_000_000}ms") logd(TAG_VM, "[selectMonthFromYearView] selectedDate set dt=${(System.nanoTime() - t0) / 1_000_000}ms")
_isYearView.value = false _isYearView.value = false
logd(TAG_VM, "[selectMonthFromYearView] isYearView=false dt=${(System.nanoTime() - t0) / 1_000_000}ms") 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() composeTraceEndSection()
logd(TAG_VM, "[selectMonthFromYearView] ===== END total=${(System.nanoTime() - t0) / 1_000_000}ms =====") 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> { private fun generateMonthDays(year: Int, month: Int): List<DayData> {
val firstOfMonth = LocalDate(year, Month(month), 1) val info = getMonthGridInfo(year, month)
val offset = firstOfMonth.dayOfWeek.ordinal return (0 until info.totalDays).map { i ->
val startDate = firstOfMonth.minus(DatePeriod(days = offset)) val date = info.startDate.plus(DatePeriod(days = i))
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))
DayData( DayData(
date = date, date = date,
isCurrentMonth = date.month.number == month && date.year == year 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.draw.alpha
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.DatePeriod import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import kotlinx.datetime.daysUntil import kotlinx.datetime.daysUntil
import kotlinx.datetime.plus import kotlinx.datetime.plus
import plus.rua.project.DayCellInfo
import plus.rua.project.LunarCache
import plus.rua.project.ShiftKind import plus.rua.project.ShiftKind
import plus.rua.project.composeTraceBeginSection import plus.rua.project.composeTraceBeginSection
import plus.rua.project.composeTraceEndSection import plus.rua.project.composeTraceEndSection
@ -99,11 +96,6 @@ fun WeekPager(
) { ) {
(0 until 7).forEach { dayOffset -> (0 until 7).forEach { dayOffset ->
val date = weekMonday.plus(DatePeriod(days = dayOffset)) val date = weekMonday.plus(DatePeriod(days = dayOffset))
val lunarData = remember(date) {
runBlocking {
LunarCache.default.getOrCompute(date)
}
}
DayCell( DayCell(
date = date, date = date,
isCurrentMonth = date.month == selectedDate.month isCurrentMonth = date.month == selectedDate.month
@ -115,8 +107,7 @@ fun WeekPager(
cellIndex = dayOffset, cellIndex = dayOffset,
onClick = { onDateClick(date) }, onClick = { onDateClick(date) },
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
interactionSource = interactionSource, interactionSource = interactionSource
lunarData = lunarData
) )
} }
} }

View File

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

View File

@ -6,13 +6,22 @@ import plus.rua.project.shared.BuildConfig
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun logd(tag: String, message: () -> String) { inline fun logd(tag: String, message: () -> String) {
if (BuildConfig.DEBUG) { 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") @Suppress("NOTHING_TO_INLINE")
inline fun logd(tag: String, message: String) { inline fun logd(tag: String, message: String) {
if (BuildConfig.DEBUG) { 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) assertFalse(createViewModel().isYearView.value)
} }
@Test
fun init_yearViewProgressDefaultsZero() {
assertEquals(0f, createViewModel().yearViewProgress.value, 0.001f)
}
@Test @Test
fun init_yearViewYearDefaultsToTodayYear() { fun init_yearViewYearDefaultsToTodayYear() {
assertEquals(2026, createViewModel().yearViewYear.value) assertEquals(2026, createViewModel().yearViewYear.value)
@ -375,7 +370,6 @@ class CalendarViewModelStateTest {
assertFalse(vm.isYearView.value) assertFalse(vm.isYearView.value)
vm.toggleYearView() vm.toggleYearView()
assertTrue(vm.isYearView.value) assertTrue(vm.isYearView.value)
assertEquals(1f, vm.yearViewProgress.value, 0.001f)
} }
@Test @Test
@ -385,7 +379,6 @@ class CalendarViewModelStateTest {
assertTrue(vm.isYearView.value) assertTrue(vm.isYearView.value)
vm.toggleYearView() vm.toggleYearView()
assertFalse(vm.isYearView.value) assertFalse(vm.isYearView.value)
assertEquals(0f, vm.yearViewProgress.value, 0.001f)
} }
@Test @Test