Commit 7ab533274ef2ae35f10d03d2e2bf15fb3d1011dc

Authored by 常守达
1 parent 31cbe47b

feat(today): 活动目标,分享

@@ -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