Commit 335cf3f5c04ae84e973bba0f17fdf476e94db479
1 parent
41f680fe
feat(ui):修复睡眠平均评分、themepreview页面错误
Showing
6 changed files
with
65 additions
and
31 deletions
| @@ -39,7 +39,6 @@ class CustomWatchThemePreviewView | @@ -39,7 +39,6 @@ class CustomWatchThemePreviewView | ||
| 39 | return WatchThemeHeader( | 39 | return WatchThemeHeader( |
| 40 | title: controller.themeItem.title, | 40 | title: controller.themeItem.title, |
| 41 | themeImageUrl: controller.themeItem.energeticImage, | 41 | themeImageUrl: controller.themeItem.energeticImage, |
| 42 | - errorDesc: null, | ||
| 43 | ); | 42 | ); |
| 44 | }), | 43 | }), |
| 45 | const SizedBox(height: 26), | 44 | const SizedBox(height: 26), |
| @@ -26,7 +26,6 @@ class WatchThemePreviewView extends GetView<WatchThemePreviewController> { | @@ -26,7 +26,6 @@ class WatchThemePreviewView extends GetView<WatchThemePreviewController> { | ||
| 26 | Obx(() { | 26 | Obx(() { |
| 27 | return WatchThemeHeader( | 27 | return WatchThemeHeader( |
| 28 | themeImageUrl: controller.themeItem.energeticImage, | 28 | themeImageUrl: controller.themeItem.energeticImage, |
| 29 | - errorDesc: null, | ||
| 30 | ); | 29 | ); |
| 31 | }), | 30 | }), |
| 32 | const SizedBox(height: 26), | 31 | const SizedBox(height: 26), |
| 1 | -import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; | ||
| 2 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
| 3 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; | 2 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; |
| 4 | -import 'package:get/get.dart'; | ||
| 5 | 3 | ||
| 6 | import 'watch_face_preview.dart'; | 4 | import 'watch_face_preview.dart'; |
| 7 | import 'watch_theme_colors.dart'; | 5 | import 'watch_theme_colors.dart'; |
| @@ -10,19 +8,15 @@ class WatchThemeHeader extends StatelessWidget { | @@ -10,19 +8,15 @@ class WatchThemeHeader extends StatelessWidget { | ||
| 10 | const WatchThemeHeader({ | 8 | const WatchThemeHeader({ |
| 11 | super.key, | 9 | super.key, |
| 12 | this.title, | 10 | this.title, |
| 13 | - this.errorDesc, | ||
| 14 | this.themeImageUrl, | 11 | this.themeImageUrl, |
| 15 | }); | 12 | }); |
| 16 | 13 | ||
| 17 | 14 | ||
| 18 | final String? title; | 15 | final String? title; |
| 19 | - final String? errorDesc; | ||
| 20 | final String? themeImageUrl; | 16 | final String? themeImageUrl; |
| 21 | 17 | ||
| 22 | @override | 18 | @override |
| 23 | Widget build(BuildContext context) { | 19 | Widget build(BuildContext context) { |
| 24 | - final environmentConfig = Get.find<AppEnvironmentConfig>(); | ||
| 25 | - | ||
| 26 | return Column( | 20 | return Column( |
| 27 | children: [ | 21 | children: [ |
| 28 | SizedBox(height: 12), | 22 | SizedBox(height: 12), |
| @@ -42,18 +36,6 @@ class WatchThemeHeader extends StatelessWidget { | @@ -42,18 +36,6 @@ class WatchThemeHeader extends StatelessWidget { | ||
| 42 | height: 1.25, | 36 | height: 1.25, |
| 43 | ), | 37 | ), |
| 44 | ), | 38 | ), |
| 45 | - if (environmentConfig.isDebug && errorDesc != null) ...[ | ||
| 46 | - SizedBox(height: 8), | ||
| 47 | - Text( | ||
| 48 | - errorDesc!, | ||
| 49 | - style: TextStyle( | ||
| 50 | - color: WatchThemeColors.pro, | ||
| 51 | - fontSize: 14, | ||
| 52 | - fontWeight: FontWeight.w400, | ||
| 53 | - ), | ||
| 54 | - maxLines: 2, | ||
| 55 | - ), | ||
| 56 | - ], | ||
| 57 | ], | 39 | ], |
| 58 | ); | 40 | ); |
| 59 | } | 41 | } |
| @@ -90,8 +90,8 @@ class LocalHealthDataConvert { | @@ -90,8 +90,8 @@ class LocalHealthDataConvert { | ||
| 90 | avgSleepScore: _averageOrNull( | 90 | avgSleepScore: _averageOrNull( |
| 91 | validSleep.map((e) => e.score).whereType<num>().toList(), | 91 | validSleep.map((e) => e.score).whereType<num>().toList(), |
| 92 | ), | 92 | ), |
| 93 | - avgSleepEvaluate: _averageOrNull( | ||
| 94 | - validSleep.map((e) => e.state?.value).whereType<num>().toList(), | 93 | + avgSleepEvaluate: _sleepEvaluateFromAverageScore( |
| 94 | + validSleep.map((e) => e.score).whereType<num>().toList(), | ||
| 95 | ), | 95 | ), |
| 96 | qoqAvgSleepDuration: _averageOrNull( | 96 | qoqAvgSleepDuration: _averageOrNull( |
| 97 | previousValidSleep.map((e) => e.durationSeconds).toList(), | 97 | previousValidSleep.map((e) => e.durationSeconds).toList(), |
| @@ -99,8 +99,8 @@ class LocalHealthDataConvert { | @@ -99,8 +99,8 @@ class LocalHealthDataConvert { | ||
| 99 | qoqSleepScore: _averageOrNull( | 99 | qoqSleepScore: _averageOrNull( |
| 100 | previousValidSleep.map((e) => e.score).whereType<num>().toList(), | 100 | previousValidSleep.map((e) => e.score).whereType<num>().toList(), |
| 101 | ), | 101 | ), |
| 102 | - qoqSleepEvaluate: _averageOrNull( | ||
| 103 | - previousValidSleep.map((e) => e.state?.value).whereType<num>().toList(), | 102 | + qoqSleepEvaluate: _sleepEvaluateFromAverageScore( |
| 103 | + previousValidSleep.map((e) => e.score).whereType<num>().toList(), | ||
| 104 | ), | 104 | ), |
| 105 | sleepTrendList: [ | 105 | sleepTrendList: [ |
| 106 | for (final item in daily) | 106 | for (final item in daily) |
| @@ -176,8 +176,8 @@ class LocalHealthDataConvert { | @@ -176,8 +176,8 @@ class LocalHealthDataConvert { | ||
| 176 | avgSleepScore: _averageOrNull( | 176 | avgSleepScore: _averageOrNull( |
| 177 | validSleep.map((e) => e.sleepScore).toList(), | 177 | validSleep.map((e) => e.sleepScore).toList(), |
| 178 | ), | 178 | ), |
| 179 | - avgSleepEvaluate: _averageOrNull( | ||
| 180 | - validSleep.map((e) => e.sleepStateValue).whereType<num>().toList(), | 179 | + avgSleepEvaluate: _sleepEvaluateFromAverageScore( |
| 180 | + validSleep.map((e) => e.sleepScore).toList(), | ||
| 181 | ), | 181 | ), |
| 182 | qoqAvgSleepDuration: _averageOrNull( | 182 | qoqAvgSleepDuration: _averageOrNull( |
| 183 | previousValidSleep.map((e) => e.sleepMinutes * 60).toList(), | 183 | previousValidSleep.map((e) => e.sleepMinutes * 60).toList(), |
| @@ -185,11 +185,8 @@ class LocalHealthDataConvert { | @@ -185,11 +185,8 @@ class LocalHealthDataConvert { | ||
| 185 | qoqSleepScore: _averageOrNull( | 185 | qoqSleepScore: _averageOrNull( |
| 186 | previousValidSleep.map((e) => e.sleepScore).toList(), | 186 | previousValidSleep.map((e) => e.sleepScore).toList(), |
| 187 | ), | 187 | ), |
| 188 | - qoqSleepEvaluate: _averageOrNull( | ||
| 189 | - previousValidSleep | ||
| 190 | - .map((e) => e.sleepStateValue) | ||
| 191 | - .whereType<num>() | ||
| 192 | - .toList(), | 188 | + qoqSleepEvaluate: _sleepEvaluateFromAverageScore( |
| 189 | + previousValidSleep.map((e) => e.sleepScore).toList(), | ||
| 193 | ), | 190 | ), |
| 194 | sleepTrendList: [ | 191 | sleepTrendList: [ |
| 195 | for (var index = 0; index < days.length; index++) | 192 | for (var index = 0; index < days.length; index++) |
| @@ -993,6 +990,12 @@ class LocalHealthDataConvert { | @@ -993,6 +990,12 @@ class LocalHealthDataConvert { | ||
| 993 | return _sum(values) / values.length; | 990 | return _sum(values) / values.length; |
| 994 | } | 991 | } |
| 995 | 992 | ||
| 993 | + static int? _sleepEvaluateFromAverageScore(List<num> scores) { | ||
| 994 | + final averageScore = _averageOrNull(scores); | ||
| 995 | + if (averageScore == null) return null; | ||
| 996 | + return HealthSleepCalculator.sleepState(averageScore.round())?.value; | ||
| 997 | + } | ||
| 998 | + | ||
| 996 | static num? _maxOrNull(List<num> values) { | 999 | static num? _maxOrNull(List<num> values) { |
| 997 | if (values.isEmpty) return null; | 1000 | if (values.isEmpty) return null; |
| 998 | return values.reduce(math.max); | 1001 | return values.reduce(math.max); |
| 1 | +import 'package:doublefeel_flutter/core/services/health_sleep_calculator.dart'; | ||
| 2 | +import 'package:flutter_test/flutter_test.dart'; | ||
| 3 | + | ||
| 4 | +void main() { | ||
| 5 | + group('HealthSleepCalculator sleep state', () { | ||
| 6 | + test('uses 60 and 85 as score boundaries', () { | ||
| 7 | + expect(HealthSleepCalculator.sleepState(0), isNull); | ||
| 8 | + expect(HealthSleepCalculator.sleepState(59), HealthSleepState.poor); | ||
| 9 | + expect(HealthSleepCalculator.sleepState(60), HealthSleepState.good); | ||
| 10 | + expect(HealthSleepCalculator.sleepState(84), HealthSleepState.good); | ||
| 11 | + expect(HealthSleepCalculator.sleepState(85), HealthSleepState.great); | ||
| 12 | + }); | ||
| 13 | + }); | ||
| 14 | +} |
| @@ -82,6 +82,43 @@ void main() { | @@ -82,6 +82,43 @@ void main() { | ||
| 82 | expect(statistics.avgSleepDuration, isNull); | 82 | expect(statistics.avgSleepDuration, isNull); |
| 83 | }); | 83 | }); |
| 84 | 84 | ||
| 85 | + test('maps average sleep score to average sleep evaluate', () { | ||
| 86 | + final firstDay = DateTime(2026, 7, 14); | ||
| 87 | + final secondDay = DateTime(2026, 7, 15); | ||
| 88 | + final thirdDay = DateTime(2026, 7, 16); | ||
| 89 | + | ||
| 90 | + HealthRawSleepResult result(DateTime day, int score, int state) { | ||
| 91 | + final endTime = LocalHealthDataConvert.unixSeconds( | ||
| 92 | + day.add(const Duration(hours: 7)), | ||
| 93 | + ); | ||
| 94 | + return HealthRawSleepResult( | ||
| 95 | + userId: 1, | ||
| 96 | + date: endTime, | ||
| 97 | + startDate: endTime - const Duration(hours: 7).inSeconds, | ||
| 98 | + sleepScore: score, | ||
| 99 | + sleepState: state, | ||
| 100 | + inBedMinutes: 430, | ||
| 101 | + awakMinutes: 20, | ||
| 102 | + sleepMinutes: 400, | ||
| 103 | + ); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + final statistics = LocalHealthDataConvert.sleepStatisticsFromResults( | ||
| 107 | + dateRangeType: 0, | ||
| 108 | + days: [firstDay, secondDay, thirdDay], | ||
| 109 | + previousDays: const [], | ||
| 110 | + sleepResults: [ | ||
| 111 | + result(firstDay, 50, 3), | ||
| 112 | + result(secondDay, 50, 3), | ||
| 113 | + result(thirdDay, 90, 1), | ||
| 114 | + ], | ||
| 115 | + heartRate: const [], | ||
| 116 | + ); | ||
| 117 | + | ||
| 118 | + expect(statistics.avgSleepScore, closeTo(63.333, 0.001)); | ||
| 119 | + expect(statistics.avgSleepEvaluate, 2); | ||
| 120 | + }); | ||
| 121 | + | ||
| 85 | test('sleep duration excludes awake intervals inside sleep range', () { | 122 | test('sleep duration excludes awake intervals inside sleep range', () { |
| 86 | final day = DateTime(2026, 7, 16); | 123 | final day = DateTime(2026, 7, 16); |
| 87 | final sleepStart = DateTime(2026, 7, 15, 23, 56); | 124 | final sleepStart = DateTime(2026, 7, 15, 23, 56); |
-
Please register or login to post a comment