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:
parent
e249700ee5
commit
829f89eb7c
@ -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 =====")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user