Showing
17 changed files
with
231 additions
and
67 deletions
| @@ -17,6 +17,8 @@ PODS: | @@ -17,6 +17,8 @@ PODS: | ||
| 17 | - FlutterMacOS | 17 | - FlutterMacOS |
| 18 | - permission_handler_apple (9.3.0): | 18 | - permission_handler_apple (9.3.0): |
| 19 | - Flutter | 19 | - Flutter |
| 20 | + - share_plus (0.0.1): | ||
| 21 | + - Flutter | ||
| 20 | - shared_preferences_foundation (0.0.1): | 22 | - shared_preferences_foundation (0.0.1): |
| 21 | - Flutter | 23 | - Flutter |
| 22 | - FlutterMacOS | 24 | - FlutterMacOS |
| @@ -36,6 +38,7 @@ DEPENDENCIES: | @@ -36,6 +38,7 @@ DEPENDENCIES: | ||
| 36 | - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) | 38 | - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) |
| 37 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) | 39 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) |
| 38 | - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) | 40 | - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) |
| 41 | + - share_plus (from `.symlinks/plugins/share_plus/ios`) | ||
| 39 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) | 42 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) |
| 40 | - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) | 43 | - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) |
| 41 | - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`) | 44 | - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`) |
| @@ -58,6 +61,8 @@ EXTERNAL SOURCES: | @@ -58,6 +61,8 @@ EXTERNAL SOURCES: | ||
| 58 | :path: ".symlinks/plugins/path_provider_foundation/darwin" | 61 | :path: ".symlinks/plugins/path_provider_foundation/darwin" |
| 59 | permission_handler_apple: | 62 | permission_handler_apple: |
| 60 | :path: ".symlinks/plugins/permission_handler_apple/ios" | 63 | :path: ".symlinks/plugins/permission_handler_apple/ios" |
| 64 | + share_plus: | ||
| 65 | + :path: ".symlinks/plugins/share_plus/ios" | ||
| 61 | shared_preferences_foundation: | 66 | shared_preferences_foundation: |
| 62 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin" | 67 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin" |
| 63 | sqflite_darwin: | 68 | sqflite_darwin: |
| @@ -73,6 +78,7 @@ SPEC CHECKSUMS: | @@ -73,6 +78,7 @@ SPEC CHECKSUMS: | ||
| 73 | image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a | 78 | image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a |
| 74 | path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 | 79 | path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 |
| 75 | permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d | 80 | permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d |
| 81 | + share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a | ||
| 76 | shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 | 82 | shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 |
| 77 | sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 | 83 | sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 |
| 78 | TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 | 84 | TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 |
| @@ -283,7 +283,6 @@ | @@ -283,7 +283,6 @@ | ||
| 283 | 66FBE5882FDA4F0F00F515B4 /* Frameworks */, | 283 | 66FBE5882FDA4F0F00F515B4 /* Frameworks */, |
| 284 | 66FBE5892FDA4F0F00F515B4 /* Resources */, | 284 | 66FBE5892FDA4F0F00F515B4 /* Resources */, |
| 285 | 66FBE5BE2FDA518D00F515B4 /* Embed Foundation Extensions */, | 285 | 66FBE5BE2FDA518D00F515B4 /* Embed Foundation Extensions */, |
| 286 | - 1F69C09D185F42507815E3C2 /* [CP] Embed Pods Frameworks */, | ||
| 287 | ); | 286 | ); |
| 288 | buildRules = ( | 287 | buildRules = ( |
| 289 | ); | 288 | ); |
| @@ -365,23 +364,6 @@ | @@ -365,23 +364,6 @@ | ||
| 365 | /* End PBXResourcesBuildPhase section */ | 364 | /* End PBXResourcesBuildPhase section */ |
| 366 | 365 | ||
| 367 | /* Begin PBXShellScriptBuildPhase section */ | 366 | /* Begin PBXShellScriptBuildPhase section */ |
| 368 | - 1F69C09D185F42507815E3C2 /* [CP] Embed Pods Frameworks */ = { | ||
| 369 | - isa = PBXShellScriptBuildPhase; | ||
| 370 | - buildActionMask = 2147483647; | ||
| 371 | - files = ( | ||
| 372 | - ); | ||
| 373 | - inputFileListPaths = ( | ||
| 374 | - "${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-input-files.xcfilelist", | ||
| 375 | - ); | ||
| 376 | - name = "[CP] Embed Pods Frameworks"; | ||
| 377 | - outputFileListPaths = ( | ||
| 378 | - "${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-output-files.xcfilelist", | ||
| 379 | - ); | ||
| 380 | - runOnlyForDeploymentPostprocessing = 0; | ||
| 381 | - shellPath = /bin/sh; | ||
| 382 | - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks.sh\"\n"; | ||
| 383 | - showEnvVarsInLog = 0; | ||
| 384 | - }; | ||
| 385 | 4348DC7B38CB739C4ABA7DAC /* [CP] Embed Pods Frameworks */ = { | 367 | 4348DC7B38CB739C4ABA7DAC /* [CP] Embed Pods Frameworks */ = { |
| 386 | isa = PBXShellScriptBuildPhase; | 368 | isa = PBXShellScriptBuildPhase; |
| 387 | buildActionMask = 2147483647; | 369 | buildActionMask = 2147483647; |
| 1 | +import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; | ||
| 1 | import 'package:doublefeel_flutter/core/network/api/user_api.dart'; | 2 | import 'package:doublefeel_flutter/core/network/api/user_api.dart'; |
| 2 | import 'package:get/get.dart'; | 3 | import 'package:get/get.dart'; |
| 3 | 4 | ||
| @@ -7,7 +8,7 @@ class BindPartnerBinding extends Bindings { | @@ -7,7 +8,7 @@ class BindPartnerBinding extends Bindings { | ||
| 7 | @override | 8 | @override |
| 8 | void dependencies() { | 9 | void dependencies() { |
| 9 | Get.put(BindPartnerController( | 10 | Get.put(BindPartnerController( |
| 10 | - Get.find<UserApi>(), | 11 | + Get.find<FriendApi>(), |
| 11 | )); | 12 | )); |
| 12 | } | 13 | } |
| 13 | } | 14 | } |
| 1 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | 1 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
| 2 | -import 'package:doublefeel_flutter/core/network/api/user_api.dart'; | 2 | +import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; |
| 3 | import 'package:doublefeel_flutter/core/result/app_result.dart'; | 3 | import 'package:doublefeel_flutter/core/result/app_result.dart'; |
| 4 | import 'package:doublefeel_flutter/core/services/user_state_service.dart'; | 4 | import 'package:doublefeel_flutter/core/services/user_state_service.dart'; |
| 5 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; | 5 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; |
| 6 | -import 'package:doublefeel_flutter/data/models/user/user_models.dart'; | ||
| 7 | import 'package:flutter/widgets.dart'; | 6 | import 'package:flutter/widgets.dart'; |
| 8 | import 'package:get/get.dart'; | 7 | import 'package:get/get.dart'; |
| 8 | +import 'package:share_plus/share_plus.dart'; | ||
| 9 | 9 | ||
| 10 | class BindPartnerController extends GetxController { | 10 | class BindPartnerController extends GetxController { |
| 11 | - BindPartnerController(this._userApi); | 11 | + BindPartnerController(this._friendApi); |
| 12 | 12 | ||
| 13 | - final UserApi _userApi; | 13 | + final FriendApi _friendApi; |
| 14 | 14 | ||
| 15 | final TextEditingController partnerIdController = TextEditingController(); | 15 | final TextEditingController partnerIdController = TextEditingController(); |
| 16 | 16 | ||
| @@ -40,27 +40,22 @@ class BindPartnerController extends GetxController { | @@ -40,27 +40,22 @@ class BindPartnerController extends GetxController { | ||
| 40 | 40 | ||
| 41 | bool get canSubmit => partnerIdInput.value.isNotEmpty; | 41 | bool get canSubmit => partnerIdInput.value.isNotEmpty; |
| 42 | 42 | ||
| 43 | - void onShareMyCode() {} | 43 | + void onShareMyCode() { |
| 44 | + Share.share("""嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App! | ||
| 45 | +它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。 | ||
| 46 | +快来一起使用吧 👉 https://Xxxxxxx | ||
| 47 | +我的好友id:${myInviteCode.value}"""); | ||
| 48 | + } | ||
| 44 | 49 | ||
| 45 | Future<void> onSubmitPartnerId() async { | 50 | Future<void> onSubmitPartnerId() async { |
| 46 | if (!canSubmit || isSubmitting.value) return; | 51 | if (!canSubmit || isSubmitting.value) return; |
| 47 | isSubmitting.value = true; | 52 | isSubmitting.value = true; |
| 48 | try { | 53 | try { |
| 49 | - final result = await _userApi.bindPartner(partnerIdInput.value); | 54 | + final result = await _friendApi.addFriend(partnerIdInput.value); |
| 50 | if (result is AppFailure) { | 55 | if (result is AppFailure) { |
| 51 | return; | 56 | return; |
| 52 | - } else { | ||
| 53 | - final partnerResult = await _userApi.getPartnerUserInfo(); | ||
| 54 | - if (partnerResult is AppSuccess<BoundUserInfoResponse>) { | ||
| 55 | - final partner = partnerResult.data.partnerUserInfo; | ||
| 56 | - if (partner != null) { | ||
| 57 | - await Get.find<UserPreferencesStorage>() | ||
| 58 | - .updatePartnerUserInfo(partner); | ||
| 59 | - } | ||
| 60 | - } | ||
| 61 | } | 57 | } |
| 62 | final userStateService = Get.find<UserStateService>(); | 58 | final userStateService = Get.find<UserStateService>(); |
| 63 | - final partnerId = partnerIdInput.value; | ||
| 64 | if (!userStateService.isVip) { | 59 | if (!userStateService.isVip) { |
| 65 | Get.offAllNamed(AppRoutes.userOnboarding, arguments: { | 60 | Get.offAllNamed(AppRoutes.userOnboarding, arguments: { |
| 66 | 'type': "MembershipOfferPage", | 61 | 'type': "MembershipOfferPage", |
| 1 | import 'package:doublefeel_flutter/app/widget/dash_divider.dart'; | 1 | import 'package:doublefeel_flutter/app/widget/dash_divider.dart'; |
| 2 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 2 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 3 | +import 'package:doublefeel_flutter/core/util/app_toast.dart'; | ||
| 3 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; |
| 4 | import 'package:flutter/services.dart'; | 5 | import 'package:flutter/services.dart'; |
| 5 | 6 | ||
| @@ -182,6 +183,7 @@ class BindPartnerView extends GetView<BindPartnerController> { | @@ -182,6 +183,7 @@ class BindPartnerView extends GetView<BindPartnerController> { | ||
| 182 | Clipboard.setData( | 183 | Clipboard.setData( |
| 183 | ClipboardData(text: controller.myInviteCode.value), | 184 | ClipboardData(text: controller.myInviteCode.value), |
| 184 | ); | 185 | ); |
| 186 | + AppToast.show('复制成功'); | ||
| 185 | }, | 187 | }, |
| 186 | child: Container( | 188 | child: Container( |
| 187 | width: 20, | 189 | width: 20, |
| @@ -117,6 +117,7 @@ class TodayController extends GetxController { | @@ -117,6 +117,7 @@ class TodayController extends GetxController { | ||
| 117 | final v2LatestHrv = Rxn<V2LatestHrvData>(); | 117 | final v2LatestHrv = Rxn<V2LatestHrvData>(); |
| 118 | final v2HrvTrend = Rxn<V2HrvTrendData>(); | 118 | final v2HrvTrend = Rxn<V2HrvTrendData>(); |
| 119 | final v2RealtimeStress = Rxn<V2RealtimeStressData>(); | 119 | final v2RealtimeStress = Rxn<V2RealtimeStressData>(); |
| 120 | + final v2ActivityTarget = Rxn<V2ActivityTarget>(); | ||
| 120 | 121 | ||
| 121 | Future<void> requestHealthAuthorization() async { | 122 | Future<void> requestHealthAuthorization() async { |
| 122 | if (GetPlatform.isIOS) { | 123 | if (GetPlatform.isIOS) { |
| @@ -254,6 +255,14 @@ class TodayController extends GetxController { | @@ -254,6 +255,14 @@ class TodayController extends GetxController { | ||
| 254 | case AppFailure(): | 255 | case AppFailure(): |
| 255 | v2RealtimeStress.value = null; | 256 | v2RealtimeStress.value = null; |
| 256 | } | 257 | } |
| 258 | + if (v2ActivityTarget.value == null) { | ||
| 259 | + switch (await _healthApi.getV2ActivityTarget(friendUserId)) { | ||
| 260 | + case AppSuccess(:final data): | ||
| 261 | + v2ActivityTarget.value = data; | ||
| 262 | + case AppFailure(): | ||
| 263 | + v2ActivityTarget.value = null; | ||
| 264 | + } | ||
| 265 | + } | ||
| 257 | } | 266 | } |
| 258 | 267 | ||
| 259 | void _clearRealTimeData() { | 268 | void _clearRealTimeData() { |
| @@ -269,6 +278,7 @@ class TodayController extends GetxController { | @@ -269,6 +278,7 @@ class TodayController extends GetxController { | ||
| 269 | stressChartData.clear(); | 278 | stressChartData.clear(); |
| 270 | hrvAnnotations.clear(); | 279 | hrvAnnotations.clear(); |
| 271 | avgHrv.value = '--'; | 280 | avgHrv.value = '--'; |
| 281 | + v2ActivityTarget.value = null; | ||
| 272 | } | 282 | } |
| 273 | 283 | ||
| 274 | void _applyHrvStatistics(HrvStatisticsData data, DateTime date) { | 284 | void _applyHrvStatistics(HrvStatisticsData data, DateTime date) { |
| @@ -78,12 +78,7 @@ class MyTab extends GetView<MyController> { | @@ -78,12 +78,7 @@ class MyTab extends GetView<MyController> { | ||
| 78 | _SettingsRow( | 78 | _SettingsRow( |
| 79 | title: 'Route List', | 79 | title: 'Route List', |
| 80 | onTap: () { | 80 | onTap: () { |
| 81 | - Get.toNamed(Routes.FRIEND_HOME, | ||
| 82 | - arguments: FriendItem( | ||
| 83 | - friendUserId: 61, | ||
| 84 | - remarkName: '徐璐', | ||
| 85 | - avatar: | ||
| 86 | - 'https://cdn3.didiapp.com/manager_upload/ab2abeecb14442b198bdaedc7c41f094.png')); | 81 | + Get.toNamed(Routes.ROUTE_LIST); |
| 87 | }, | 82 | }, |
| 88 | ), | 83 | ), |
| 89 | ], | 84 | ], |
| @@ -208,12 +208,11 @@ class TodayTabBody extends StatelessWidget { | @@ -208,12 +208,11 @@ class TodayTabBody extends StatelessWidget { | ||
| 208 | TodayHrvChartCard(controller: controller), | 208 | TodayHrvChartCard(controller: controller), |
| 209 | const SizedBox(height: 12), | 209 | const SizedBox(height: 12), |
| 210 | _LatestHrvCard(controller: controller), | 210 | _LatestHrvCard(controller: controller), |
| 211 | - const SizedBox(height: 12), | ||
| 212 | - measureHRVButton(context), | 211 | + if (!controller.isFriend) measureHRVButton(context), |
| 213 | Padding( | 212 | Padding( |
| 214 | padding: const EdgeInsets.symmetric(vertical: 12), | 213 | padding: const EdgeInsets.symmetric(vertical: 12), |
| 215 | child: Text( | 214 | child: Text( |
| 216 | - context.l10n.dailyActions, | 215 | + controller.isFriend ? 'TA的今日行动' : context.l10n.dailyActions, |
| 217 | style: TextStyle( | 216 | style: TextStyle( |
| 218 | color: context.colors.textPrimary, | 217 | color: context.colors.textPrimary, |
| 219 | fontSize: 16, | 218 | fontSize: 16, |
| @@ -246,6 +245,7 @@ class TodayTabBody extends StatelessWidget { | @@ -246,6 +245,7 @@ class TodayTabBody extends StatelessWidget { | ||
| 246 | return GestureDetector( | 245 | return GestureDetector( |
| 247 | onTap: showHrvMeasurementBottomSheet, | 246 | onTap: showHrvMeasurementBottomSheet, |
| 248 | child: Container( | 247 | child: Container( |
| 248 | + margin: EdgeInsets.only(top: 12), | ||
| 249 | padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), | 249 | padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), |
| 250 | decoration: BoxDecoration( | 250 | decoration: BoxDecoration( |
| 251 | color: Colors.white, | 251 | color: Colors.white, |
| @@ -112,7 +112,7 @@ class TodayHrvChartCard extends StatelessWidget { | @@ -112,7 +112,7 @@ class TodayHrvChartCard extends StatelessWidget { | ||
| 112 | return Row( | 112 | return Row( |
| 113 | children: [ | 113 | children: [ |
| 114 | Text( | 114 | Text( |
| 115 | - context.l10n.todaySHrvTrend, | 115 | + controller.isFriend ? 'Ta的今日HRV趋势' : context.l10n.todaySHrvTrend, |
| 116 | style: TextStyle( | 116 | style: TextStyle( |
| 117 | color: context.colors.textPrimary, | 117 | color: context.colors.textPrimary, |
| 118 | fontSize: 16, | 118 | fontSize: 16, |
| @@ -24,7 +24,9 @@ class TodayHrvNumberCard extends StatelessWidget { | @@ -24,7 +24,9 @@ class TodayHrvNumberCard extends StatelessWidget { | ||
| 24 | // HRV | 24 | // HRV |
| 25 | Expanded( | 25 | Expanded( |
| 26 | child: _NumberItem( | 26 | child: _NumberItem( |
| 27 | - label: context.l10n.averageHrvForTheDay, | 27 | + label: controller.isFriend |
| 28 | + ? 'Ta今日的平均HRV' | ||
| 29 | + : context.l10n.averageHrvForTheDay, | ||
| 28 | value: | 30 | value: |
| 29 | controller.v2HealthData.value?.hrvAvg?.toString() ?? '--', | 31 | controller.v2HealthData.value?.hrvAvg?.toString() ?? '--', |
| 30 | unit: 'ms', | 32 | unit: 'ms', |
| @@ -33,7 +35,9 @@ class TodayHrvNumberCard extends StatelessWidget { | @@ -33,7 +35,9 @@ class TodayHrvNumberCard extends StatelessWidget { | ||
| 33 | 35 | ||
| 34 | Expanded( | 36 | Expanded( |
| 35 | child: _NumberItem( | 37 | child: _NumberItem( |
| 36 | - label: context.l10n.restingHeartRate, | 38 | + label: controller.isFriend |
| 39 | + ? 'Ta的静息心率' | ||
| 40 | + : context.l10n.restingHeartRate, | ||
| 37 | value: controller.v2HealthData.value?.lastRestingHrValue | 41 | value: controller.v2HealthData.value?.lastRestingHrValue |
| 38 | ?.toString() ?? | 42 | ?.toString() ?? |
| 39 | '--', | 43 | '--', |
| @@ -21,9 +21,11 @@ class TodaySleepCard extends StatelessWidget { | @@ -21,9 +21,11 @@ class TodaySleepCard extends StatelessWidget { | ||
| 21 | int sleepDuration = healthData?.sleepDuration ?? 0; | 21 | int sleepDuration = healthData?.sleepDuration ?? 0; |
| 22 | int sleepHours = sleepDuration ~/ 3600; | 22 | int sleepHours = sleepDuration ~/ 3600; |
| 23 | int sleepMinutes = sleepDuration % 60; | 23 | int sleepMinutes = sleepDuration % 60; |
| 24 | - Color sleepStateColor = | ||
| 25 | - healthData?.sleepStateColor(context) ?? context.colors.chartGreen; | ||
| 26 | - String sleepStateStr = healthData?.sleepStateStr() ?? '--'; | 24 | + Color sleepStateColor = sleepDuration == 0 |
| 25 | + ? context.colors.textPrimary | ||
| 26 | + : healthData?.sleepStateColor(context); | ||
| 27 | + String sleepStateStr = | ||
| 28 | + sleepDuration == 0 ? '--' : healthData?.sleepStateStr() ?? '--'; | ||
| 27 | String sleepAverageHeartRate = | 29 | String sleepAverageHeartRate = |
| 28 | (healthData?.hrAvg == null || healthData?.hrAvg == 0) | 30 | (healthData?.hrAvg == null || healthData?.hrAvg == 0) |
| 29 | ? '--' | 31 | ? '--' |
| @@ -104,31 +106,49 @@ class TodayActivityCard extends StatelessWidget { | @@ -104,31 +106,49 @@ class TodayActivityCard extends StatelessWidget { | ||
| 104 | return GestureDetector( | 106 | return GestureDetector( |
| 105 | onTap: controller.toTrendActivityPage, | 107 | onTap: controller.toTrendActivityPage, |
| 106 | child: Obx(() { | 108 | child: Obx(() { |
| 109 | + var activityTarget = controller.v2ActivityTarget.value; | ||
| 107 | var healthData = controller.v2HealthData.value; | 110 | var healthData = controller.v2HealthData.value; |
| 108 | - var activity = healthData?.move; | 111 | + var activity = healthData?.move ?? 0; |
| 109 | var stand = healthData?.stand ?? 0; | 112 | var stand = healthData?.stand ?? 0; |
| 110 | var exercise = healthData?.exercise ?? 0; | 113 | var exercise = healthData?.exercise ?? 0; |
| 111 | - var standStr = stand > 0 ? '${stand ~/ 3600}' : '--'; | ||
| 112 | - var exerciseStr = exercise > 0 ? '${exercise % 60}' : '--'; | 114 | + |
| 115 | + var activityStr = (activity > 0 && (activityTarget?.move ?? 0) > 0) | ||
| 116 | + ? '$activity' | ||
| 117 | + : '--'; | ||
| 118 | + var exerciseStr = (exercise > 0 && (activityTarget?.exercise ?? 0) > 0) | ||
| 119 | + ? '${exercise % 60}' | ||
| 120 | + : '--'; | ||
| 121 | + var standStr = (stand > 0 && (activityTarget?.stand ?? 0) > 0) | ||
| 122 | + ? '${stand ~/ 3600}' | ||
| 123 | + : '--'; | ||
| 124 | + double activityProgress = | ||
| 125 | + (activity > 0 && (activityTarget?.move ?? 0) > 0) | ||
| 126 | + ? (activity / (activityTarget?.move ?? 0)) * 360 | ||
| 127 | + : -1; | ||
| 128 | + double exerciseProgress = | ||
| 129 | + (exercise > 0 && (activityTarget?.exercise ?? 0) > 0) | ||
| 130 | + ? (exercise / (activityTarget?.exercise ?? 0)) * 360 | ||
| 131 | + : -1; | ||
| 132 | + double standProgress = (stand > 0 && (activityTarget?.stand ?? 0) > 0) | ||
| 133 | + ? (stand / (activityTarget?.stand ?? 0)) * 360 | ||
| 134 | + : -1; | ||
| 113 | 135 | ||
| 114 | return _TodaySummaryCard( | 136 | return _TodaySummaryCard( |
| 115 | iconAsset: 'assets/images/common/ic_exercise.png', | 137 | iconAsset: 'assets/images/common/ic_exercise.png', |
| 116 | - iconColor: const Color(0xFFFF5279), | 138 | + iconColor: context.colors.warning, |
| 117 | title: context.l10n.fitness, | 139 | title: context.l10n.fitness, |
| 118 | actionText: context.l10n.viewFitnessReport, | 140 | actionText: context.l10n.viewFitnessReport, |
| 119 | metrics: [ | 141 | metrics: [ |
| 120 | _MetricData( | 142 | _MetricData( |
| 121 | label: context.l10n.event, | 143 | label: context.l10n.event, |
| 122 | - labelColor: const Color(0xFFFF5279), | 144 | + labelColor: context.colors.warning, |
| 123 | valueSpans: [ | 145 | valueSpans: [ |
| 124 | - _MetricValueSpan( | ||
| 125 | - (activity == null || activity == 0) ? '--' : '$activity', | ||
| 126 | - '千卡'), | 146 | + _MetricValueSpan(activityStr, '千卡'), |
| 127 | ], | 147 | ], |
| 128 | ), | 148 | ), |
| 129 | _MetricData( | 149 | _MetricData( |
| 130 | label: context.l10n.exercise, | 150 | label: context.l10n.exercise, |
| 131 | - labelColor: const Color(0xFF3BD49D), | 151 | + labelColor: context.colors.chartGreen, |
| 132 | valueSpans: [ | 152 | valueSpans: [ |
| 133 | _MetricValueSpan( | 153 | _MetricValueSpan( |
| 134 | exerciseStr, | 154 | exerciseStr, |
| @@ -161,10 +181,10 @@ class TodayActivityCard extends StatelessWidget { | @@ -161,10 +181,10 @@ class TodayActivityCard extends StatelessWidget { | ||
| 161 | duration: const Duration(seconds: 1), | 181 | duration: const Duration(seconds: 1), |
| 162 | centerCircleSizeRatio: 0.2, | 182 | centerCircleSizeRatio: 0.2, |
| 163 | gapRatio: 0.06, | 183 | gapRatio: 0.06, |
| 164 | - sweepAngles: const [ | ||
| 165 | - 180, | ||
| 166 | - 230, | ||
| 167 | - 180, | 184 | + sweepAngles: [ |
| 185 | + activityProgress, | ||
| 186 | + exerciseProgress, | ||
| 187 | + standProgress | ||
| 168 | ], | 188 | ], |
| 169 | backgroundColors: [ | 189 | backgroundColors: [ |
| 170 | context.colors.chartPink.withOpacity(0.4), | 190 | context.colors.chartPink.withOpacity(0.4), |
| 1 | +import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | ||
| 1 | import 'package:get/get.dart'; | 2 | import 'package:get/get.dart'; |
| 2 | 3 | ||
| 3 | import '../constants/network_const.dart'; | 4 | import '../constants/network_const.dart'; |
| @@ -48,6 +49,7 @@ class AppErrorHandler { | @@ -48,6 +49,7 @@ class AppErrorHandler { | ||
| 48 | if (Get.isRegistered<UserStateService>()) { | 49 | if (Get.isRegistered<UserStateService>()) { |
| 49 | Get.find<UserStateService>().onLogout(); | 50 | Get.find<UserStateService>().onLogout(); |
| 50 | } | 51 | } |
| 52 | + Get.offAllNamed(AppRoutes.login); | ||
| 51 | } | 53 | } |
| 52 | 54 | ||
| 53 | void _showMessage(String? message) { | 55 | void _showMessage(String? message) { |
| @@ -337,4 +337,20 @@ class HealthApi { | @@ -337,4 +337,20 @@ class HealthApi { | ||
| 337 | errorHandlingPolicy: HttpErrorHandlingPolicy.defaultPolicy, | 337 | errorHandlingPolicy: HttpErrorHandlingPolicy.defaultPolicy, |
| 338 | ); | 338 | ); |
| 339 | } | 339 | } |
| 340 | + | ||
| 341 | + Future<AppResult<V2ActivityTarget>> getV2ActivityTarget( | ||
| 342 | + int? friendUserId, | ||
| 343 | + ) { | ||
| 344 | + return safeCall( | ||
| 345 | + call: () async { | ||
| 346 | + final response = await _dioClient.dio | ||
| 347 | + .get(ApiPaths.v2ActivityTarget, queryParameters: { | ||
| 348 | + if (friendUserId != null) 'query_user_id': friendUserId, | ||
| 349 | + }); | ||
| 350 | + return V2ActivityTarget.fromJson( | ||
| 351 | + response.data as Map<String, dynamic>); | ||
| 352 | + }, | ||
| 353 | + errorHandlingPolicy: HttpErrorHandlingPolicy.defaultPolicy, | ||
| 354 | + ); | ||
| 355 | + } | ||
| 340 | } | 356 | } |
| @@ -36,6 +36,8 @@ abstract final class ApiPaths { | @@ -36,6 +36,8 @@ abstract final class ApiPaths { | ||
| 36 | static const v2HrvTrend = '/client/doublefeel/health/v2/hrv_trend/'; | 36 | static const v2HrvTrend = '/client/doublefeel/health/v2/hrv_trend/'; |
| 37 | static const v2RealtimeStress = | 37 | static const v2RealtimeStress = |
| 38 | '/client/doublefeel/health/v2/realtime_stress/'; | 38 | '/client/doublefeel/health/v2/realtime_stress/'; |
| 39 | + static const v2ActivityTarget = | ||
| 40 | + '/client/doublefeel/health/v2/activity_target/'; | ||
| 39 | 41 | ||
| 40 | // Pay | 42 | // Pay |
| 41 | static const paymentProducts = '/client/doublefeel/payment/products/'; | 43 | static const paymentProducts = '/client/doublefeel/payment/products/'; |
| @@ -151,7 +151,7 @@ class V2HealthData { | @@ -151,7 +151,7 @@ class V2HealthData { | ||
| 151 | case 3: | 151 | case 3: |
| 152 | return context.colors.chartPink; | 152 | return context.colors.chartPink; |
| 153 | } | 153 | } |
| 154 | - return context.colors.chartGreen; | 154 | + return null; |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | sleepStateStr() { | 157 | sleepStateStr() { |
| @@ -318,3 +318,63 @@ class V2RealtimeStressData { | @@ -318,3 +318,63 @@ class V2RealtimeStressData { | ||
| 318 | return val; | 318 | return val; |
| 319 | } | 319 | } |
| 320 | } | 320 | } |
| 321 | + | ||
| 322 | +/// v2/activity_target/ response | ||
| 323 | +/// {"id":0,"create_time":0,"update_time":0,"user_id":131, | ||
| 324 | +/// "move":300,"step":8000,"stand":12,"exercise":3600} | ||
| 325 | +class V2ActivityTarget { | ||
| 326 | + const V2ActivityTarget({ | ||
| 327 | + this.id, | ||
| 328 | + this.userId, | ||
| 329 | + this.move, | ||
| 330 | + this.step, | ||
| 331 | + this.stand, | ||
| 332 | + this.exercise, | ||
| 333 | + this.createTime, | ||
| 334 | + this.updateTime, | ||
| 335 | + }); | ||
| 336 | + | ||
| 337 | + final int? id; | ||
| 338 | + final int? userId; | ||
| 339 | + | ||
| 340 | + /// 活动消耗目标(千卡) | ||
| 341 | + final int? move; | ||
| 342 | + | ||
| 343 | + /// 步数目标 | ||
| 344 | + final int? step; | ||
| 345 | + | ||
| 346 | + /// 站立小时目标 | ||
| 347 | + final int? stand; | ||
| 348 | + | ||
| 349 | + /// 锻炼时长目标(秒) | ||
| 350 | + final int? exercise; | ||
| 351 | + | ||
| 352 | + final int? createTime; | ||
| 353 | + final int? updateTime; | ||
| 354 | + | ||
| 355 | + factory V2ActivityTarget.fromJson(Map<String, dynamic> json) { | ||
| 356 | + return V2ActivityTarget( | ||
| 357 | + id: _parseInt(json['id']), | ||
| 358 | + userId: _parseInt(json['user_id']), | ||
| 359 | + move: _parseInt(json['move']), | ||
| 360 | + step: _parseInt(json['step']), | ||
| 361 | + stand: _parseInt(json['stand']), | ||
| 362 | + exercise: _parseInt(json['exercise']), | ||
| 363 | + createTime: _parseInt(json['create_time']), | ||
| 364 | + updateTime: _parseInt(json['update_time']), | ||
| 365 | + ); | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + Map<String, dynamic> toJson() { | ||
| 369 | + final val = <String, dynamic>{}; | ||
| 370 | + if (id != null) val['id'] = id; | ||
| 371 | + if (userId != null) val['user_id'] = userId; | ||
| 372 | + if (move != null) val['move'] = move; | ||
| 373 | + if (step != null) val['step'] = step; | ||
| 374 | + if (stand != null) val['stand'] = stand; | ||
| 375 | + if (exercise != null) val['exercise'] = exercise; | ||
| 376 | + if (createTime != null) val['create_time'] = createTime; | ||
| 377 | + if (updateTime != null) val['update_time'] = updateTime; | ||
| 378 | + return val; | ||
| 379 | + } | ||
| 380 | +} |
| @@ -650,7 +650,7 @@ packages: | @@ -650,7 +650,7 @@ packages: | ||
| 650 | source: hosted | 650 | source: hosted |
| 651 | version: "1.9.1" | 651 | version: "1.9.1" |
| 652 | path_provider: | 652 | path_provider: |
| 653 | - dependency: transitive | 653 | + dependency: "direct overridden" |
| 654 | description: | 654 | description: |
| 655 | name: path_provider | 655 | name: path_provider |
| 656 | sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" | 656 | sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" |
| @@ -819,6 +819,24 @@ packages: | @@ -819,6 +819,24 @@ packages: | ||
| 819 | url: "https://pub.dev" | 819 | url: "https://pub.dev" |
| 820 | source: hosted | 820 | source: hosted |
| 821 | version: "0.28.0" | 821 | version: "0.28.0" |
| 822 | + share_plus: | ||
| 823 | + dependency: "direct main" | ||
| 824 | + description: | ||
| 825 | + path: "packages/share_plus/share_plus" | ||
| 826 | + ref: "br_share_plus-v10.1.1_ohos" | ||
| 827 | + resolved-ref: "55de300a8627c55cd45ac86e6a26bcae8e0ca4cf" | ||
| 828 | + url: "https://gitcode.com/CPF-Flutter/flutter_plus_plugins.git" | ||
| 829 | + source: git | ||
| 830 | + version: "10.1.1" | ||
| 831 | + share_plus_platform_interface: | ||
| 832 | + dependency: transitive | ||
| 833 | + description: | ||
| 834 | + path: "packages/share_plus/share_plus_platform_interface" | ||
| 835 | + ref: "55de300a8627c55cd45ac86e6a26bcae8e0ca4cf" | ||
| 836 | + resolved-ref: "55de300a8627c55cd45ac86e6a26bcae8e0ca4cf" | ||
| 837 | + url: "https://gitcode.com/CPF-Flutter/flutter_plus_plugins.git" | ||
| 838 | + source: git | ||
| 839 | + version: "5.0.1" | ||
| 822 | shared_preferences: | 840 | shared_preferences: |
| 823 | dependency: "direct main" | 841 | dependency: "direct main" |
| 824 | description: | 842 | description: |
| @@ -1042,6 +1060,38 @@ packages: | @@ -1042,6 +1060,38 @@ packages: | ||
| 1042 | url: "https://pub.dev" | 1060 | url: "https://pub.dev" |
| 1043 | source: hosted | 1061 | source: hosted |
| 1044 | version: "1.4.0" | 1062 | version: "1.4.0" |
| 1063 | + url_launcher_linux: | ||
| 1064 | + dependency: transitive | ||
| 1065 | + description: | ||
| 1066 | + name: url_launcher_linux | ||
| 1067 | + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a | ||
| 1068 | + url: "https://pub.dev" | ||
| 1069 | + source: hosted | ||
| 1070 | + version: "3.2.2" | ||
| 1071 | + url_launcher_platform_interface: | ||
| 1072 | + dependency: transitive | ||
| 1073 | + description: | ||
| 1074 | + name: url_launcher_platform_interface | ||
| 1075 | + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" | ||
| 1076 | + url: "https://pub.dev" | ||
| 1077 | + source: hosted | ||
| 1078 | + version: "2.3.2" | ||
| 1079 | + url_launcher_web: | ||
| 1080 | + dependency: transitive | ||
| 1081 | + description: | ||
| 1082 | + name: url_launcher_web | ||
| 1083 | + sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" | ||
| 1084 | + url: "https://pub.dev" | ||
| 1085 | + source: hosted | ||
| 1086 | + version: "2.4.3" | ||
| 1087 | + url_launcher_windows: | ||
| 1088 | + dependency: transitive | ||
| 1089 | + description: | ||
| 1090 | + name: url_launcher_windows | ||
| 1091 | + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" | ||
| 1092 | + url: "https://pub.dev" | ||
| 1093 | + source: hosted | ||
| 1094 | + version: "3.1.5" | ||
| 1045 | uuid: | 1095 | uuid: |
| 1046 | dependency: transitive | 1096 | dependency: transitive |
| 1047 | description: | 1097 | description: |
| @@ -1143,6 +1193,14 @@ packages: | @@ -1143,6 +1193,14 @@ packages: | ||
| 1143 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" | 1193 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" |
| 1144 | source: git | 1194 | source: git |
| 1145 | version: "3.22.0" | 1195 | version: "3.22.0" |
| 1196 | + win32: | ||
| 1197 | + dependency: transitive | ||
| 1198 | + description: | ||
| 1199 | + name: win32 | ||
| 1200 | + sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" | ||
| 1201 | + url: "https://pub.dev" | ||
| 1202 | + source: hosted | ||
| 1203 | + version: "5.13.0" | ||
| 1146 | xdg_directories: | 1204 | xdg_directories: |
| 1147 | dependency: transitive | 1205 | dependency: transitive |
| 1148 | description: | 1206 | description: |
| @@ -1160,5 +1218,5 @@ packages: | @@ -1160,5 +1218,5 @@ packages: | ||
| 1160 | source: hosted | 1218 | source: hosted |
| 1161 | version: "3.1.3" | 1219 | version: "3.1.3" |
| 1162 | sdks: | 1220 | sdks: |
| 1163 | - dart: ">=3.8.0-0 <4.0.0" | ||
| 1164 | - flutter: ">=3.27.0" | 1221 | + dart: ">=3.10.0 <4.0.0" |
| 1222 | + flutter: ">=3.38.0" |
| @@ -42,12 +42,23 @@ dependencies: | @@ -42,12 +42,23 @@ dependencies: | ||
| 42 | url: https://gitcode.com/openharmony-sig/flutter_permission_handler.git | 42 | url: https://gitcode.com/openharmony-sig/flutter_permission_handler.git |
| 43 | path: permission_handler_ohos | 43 | path: permission_handler_ohos |
| 44 | ref: br_permission_handler_v11.3.1_ohos | 44 | ref: br_permission_handler_v11.3.1_ohos |
| 45 | + share_plus: | ||
| 46 | + git: | ||
| 47 | + url: https://gitcode.com/CPF-Flutter/flutter_plus_plugins.git | ||
| 48 | + path: packages/share_plus/share_plus | ||
| 49 | + ref: br_share_plus-v10.1.1_ohos | ||
| 45 | table_calendar: ^3.1.3 | 50 | table_calendar: ^3.1.3 |
| 46 | fluttertoast: ^8.2.2 | 51 | fluttertoast: ^8.2.2 |
| 47 | flutter_localizations: | 52 | flutter_localizations: |
| 48 | sdk: flutter | 53 | sdk: flutter |
| 49 | intl: any | 54 | intl: any |
| 50 | 55 | ||
| 56 | +# share_plus (git) 间接依赖 path_provider 的 git 版本, | ||
| 57 | +# 与 cached_network_image → flutter_cache_manager 依赖的 hosted 版本冲突。 | ||
| 58 | +# 强制使用 hosted 版本统一来源。 | ||
| 59 | +dependency_overrides: | ||
| 60 | + path_provider: ^2.1.5 | ||
| 61 | + | ||
| 51 | dev_dependencies: | 62 | dev_dependencies: |
| 52 | flutter_test: | 63 | flutter_test: |
| 53 | sdk: flutter | 64 | sdk: flutter |
-
Please register or login to post a comment