fix: 统一运动历史空态文字颜色并补充页面预览与顺序断言

This commit is contained in:
2026-09-10 11:20:51 +08:00
parent 031aea0fa2
commit 58573f4657
3 changed files with 82 additions and 3 deletions
@@ -15,12 +15,16 @@ 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.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.placeholder.ringhealth.AppViewModelProvider import com.placeholder.ringhealth.AppViewModelProvider
import com.placeholder.ringhealth.R import com.placeholder.ringhealth.R
import com.placeholder.ringhealth.core.designsystem.MetricGreen import com.placeholder.ringhealth.core.designsystem.MetricGreen
import com.placeholder.ringhealth.core.designsystem.RingHealthTheme
import com.placeholder.ringhealth.core.designsystem.TextSecondary
import com.placeholder.ringhealth.domain.model.WorkoutRecord
/** 运动历史子页:展示全部运动记录。 */ /** 运动历史子页:展示全部运动记录。 */
@Composable @Composable
@@ -29,7 +33,15 @@ fun WorkoutHistoryScreen(
viewModel: WorkoutViewModel = viewModel(factory = AppViewModelProvider.Factory), viewModel: WorkoutViewModel = viewModel(factory = AppViewModelProvider.Factory),
) { ) {
val state by viewModel.uiState.collectAsStateWithLifecycle() val state by viewModel.uiState.collectAsStateWithLifecycle()
WorkoutHistoryContent(state = state, modifier = modifier)
}
/** 无状态运动历史内容,便于 Preview。 */
@Composable
private fun WorkoutHistoryContent(
state: WorkoutUiState,
modifier: Modifier = Modifier,
) {
// 必须先判 loading 再判空(ledger Ruling R25):Mock 仓库有 200ms 延迟, // 必须先判 loading 再判空(ledger Ruling R25):Mock 仓库有 200ms 延迟,
// records 在加载完成前就是 emptyList,若先判空会先闪一下「暂无运动记录」, // records 在加载完成前就是 emptyList,若先判空会先闪一下「暂无运动记录」,
// 数据到了再跳成列表 —— 用户看到的是「明明有记录却说没有」。 // 数据到了再跳成列表 —— 用户看到的是「明明有记录却说没有」。
@@ -47,7 +59,7 @@ fun WorkoutHistoryScreen(
Text( Text(
text = stringResource(R.string.workout_empty), text = stringResource(R.string.workout_empty),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = TextSecondary,
) )
} }
return return
@@ -62,3 +74,34 @@ fun WorkoutHistoryScreen(
} }
} }
} }
/** 预览入口:以固定示例数据渲染 [WorkoutHistoryContent],便于在 IDE 中免真机查看历史页效果。 */
@Preview(showBackground = true)
@Composable
private fun WorkoutHistoryContentPreview() {
RingHealthTheme {
WorkoutHistoryContent(
state = WorkoutUiState(
loading = false,
records = listOf(
WorkoutRecord(
id = "preview-0",
type = "户外跑步",
startTime = 1_757_000_000_000L,
durationMinutes = 42,
distanceKm = 5.20,
calories = 286,
),
WorkoutRecord(
id = "preview-1",
type = "健走",
startTime = 1_756_900_000_000L,
durationMinutes = 31,
distanceKm = 2.80,
calories = 143,
),
),
),
)
}
}
@@ -23,6 +23,7 @@ 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.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@@ -34,6 +35,7 @@ import com.placeholder.ringhealth.core.common.formatDate
import com.placeholder.ringhealth.core.common.formatDistance import com.placeholder.ringhealth.core.common.formatDistance
import com.placeholder.ringhealth.core.common.formatDuration import com.placeholder.ringhealth.core.common.formatDuration
import com.placeholder.ringhealth.core.designsystem.MetricGreen import com.placeholder.ringhealth.core.designsystem.MetricGreen
import com.placeholder.ringhealth.core.designsystem.RingHealthTheme
import com.placeholder.ringhealth.core.designsystem.TextSecondary import com.placeholder.ringhealth.core.designsystem.TextSecondary
import com.placeholder.ringhealth.core.designsystem.component.HealthCard import com.placeholder.ringhealth.core.designsystem.component.HealthCard
import com.placeholder.ringhealth.core.designsystem.component.SectionHeader import com.placeholder.ringhealth.core.designsystem.component.SectionHeader
@@ -139,3 +141,35 @@ internal fun WorkoutRecordCard(record: WorkoutRecord, modifier: Modifier = Modif
} }
} }
} }
/** 预览入口:以固定示例数据渲染 [WorkoutContent],便于在 IDE 中免真机查看运动页效果。 */
@Preview(showBackground = true)
@Composable
private fun WorkoutContentPreview() {
RingHealthTheme {
WorkoutContent(
state = WorkoutUiState(
loading = false,
records = listOf(
WorkoutRecord(
id = "preview-0",
type = "户外跑步",
startTime = 1_757_000_000_000L,
durationMinutes = 42,
distanceKm = 5.20,
calories = 286,
),
WorkoutRecord(
id = "preview-1",
type = "健走",
startTime = 1_756_900_000_000L,
durationMinutes = 31,
distanceKm = 2.80,
calories = 143,
),
),
),
onOpenHistory = {},
)
}
}
@@ -12,6 +12,7 @@ import kotlinx.coroutines.test.setMain
import org.junit.After import org.junit.After
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@@ -46,7 +47,8 @@ class WorkoutViewModelTest {
val state = viewModel.uiState.value val state = viewModel.uiState.value
assertFalse(state.loading) assertFalse(state.loading)
assertEquals(5, state.records.size) assertEquals(5, state.records.size)
// Mock 的运动记录是倒序生成的,最近的在最前 // 列表必须按时间倒序(最近的在最前):直接比首尾时间戳,而不是比记录类型
assertEquals("户外跑步", state.records.first().type) // —— Mock 数据的首尾类型恰好相同,用类型断言无法察觉顺序被反转。
assertTrue(state.records.first().startTime > state.records.last().startTime)
} }
} }