Commit ce6f54be3581d43afa5dd77544e890d263838567

Authored by 权海
1 parent ed5df73e

feat(ui):修复首页卡片点击异常

@@ -100,6 +100,7 @@ class TodayController extends GetMaterialController { @@ -100,6 +100,7 @@ class TodayController extends GetMaterialController {
100 final showHealthDataAuthCardStatus = 1.obs; 100 final showHealthDataAuthCardStatus = 1.obs;
101 final stressSubtitle = ''.obs; 101 final stressSubtitle = ''.obs;
102 bool _isFirstLoad = true; 102 bool _isFirstLoad = true;
  103 + bool _isHandlingHealthAuthorizationTap = false;
103 104
104 // ── HRV 表盘引导 Banner ─────────────────── 105 // ── HRV 表盘引导 Banner ───────────────────
105 final showHrvAdBanner = false.obs; 106 final showHrvAdBanner = false.obs;
@@ -133,6 +134,8 @@ class TodayController extends GetMaterialController { @@ -133,6 +134,8 @@ class TodayController extends GetMaterialController {
133 final v2RealtimeStress = Rxn<V2RealtimeStressData>(); 134 final v2RealtimeStress = Rxn<V2RealtimeStressData>();
134 final v2ActivityTarget = Rxn<V2ActivityTarget>(); 135 final v2ActivityTarget = Rxn<V2ActivityTarget>();
135 136
  137 + int _currentStatus = 0;
  138 +
136 @override 139 @override
137 void onReady() { 140 void onReady() {
138 super.onReady(); 141 super.onReady();
@@ -160,9 +163,11 @@ class TodayController extends GetMaterialController { @@ -160,9 +163,11 @@ class TodayController extends GetMaterialController {
160 163
161 unawaited(loadDataForDate(today)); 164 unawaited(loadDataForDate(today));
162 165
  166 + // _checkHealthAuthStatus();
  167 +
163 checkAddFriendVisible(); 168 checkAddFriendVisible();
164 checkHrvAdBannerVisible(); 169 checkHrvAdBannerVisible();
165 - checkHealthDataAuthCardVisible(); 170 + checkHealthDataAuthCardVisible(shouldCaculateAndUpload: true);
166 checkNotificationAuthorizationBannerVisible(); 171 checkNotificationAuthorizationBannerVisible();
167 checkNotificationCardVisible(); 172 checkNotificationCardVisible();
168 if (!Get.find<UserStateService>().isVip) { 173 if (!Get.find<UserStateService>().isVip) {
@@ -290,40 +295,66 @@ class TodayController extends GetMaterialController { @@ -290,40 +295,66 @@ class TodayController extends GetMaterialController {
290 } 295 }
291 } 296 }
292 297
293 - Future<void> requestHealthAuthorization( 298 + Future<void> _checkHealthAuthStatus(
  299 + {bool isPullToRefresh = false,
  300 + bool shouldCaculateAndUpload = false}) async {
  301 + final result = await _hostApi.checkHealthAppAuthorization();
  302 + _currentStatus = result.status;
  303 + _refreshHealthDataAuthCardStatus(status: _currentStatus);
  304 + if (_currentStatus != 1) {
  305 + return;
  306 + }
  307 + if (!shouldCaculateAndUpload) {
  308 + return;
  309 + }
  310 + _performCaculateAndUpload(isPullToRefresh: isPullToRefresh);
  311 + }
  312 +
  313 + _performCaculateAndUpload({
  314 + bool isPullToRefresh = false,
  315 + }) {
  316 + _performHealthDataUpload();
  317 + _calculateHealthDataWithoutLoading(
  318 + uploaded: () async {
  319 + _refreshHealthDataAuthCardStatus(status: 1);
  320 + },
  321 + isPullToRefresh: isPullToRefresh,
  322 + );
  323 + }
  324 +
  325 + Future<bool> requestHealthAuthorization(
294 {bool isFromNoPermissionPage = false}) async { 326 {bool isFromNoPermissionPage = false}) async {
295 if (GetPlatform.isIOS) { 327 if (GetPlatform.isIOS) {
296 try { 328 try {
297 - final result = await _hostApi.checkHealthAppAuthorization();  
298 - AppLogger.d("checkHealthAppAuthorization : $result");  
299 - if (result.status == 0) { 329 + await _checkHealthAuthStatus();
  330 + AppLogger.d("checkHealthAppAuthorization : $_currentStatus");
  331 + if (_currentStatus == 0) {
300 bool success = await _hostApi.requestHealthClientAuthorization(); 332 bool success = await _hostApi.requestHealthClientAuthorization();
301 AppLogger.d("requestHealthClientAuthorization : $success"); 333 AppLogger.d("requestHealthClientAuthorization : $success");
302 if (success) { 334 if (success) {
  335 + _currentStatus = 1;
  336 + _refreshHealthDataAuthCardStatus(status: 1);
303 // await LoadingService.instance.run(() async { 337 // await LoadingService.instance.run(() async {
304 - _performHealthDataUpload();  
305 - _startCoreCaculate(refreshAfterComplete: true); 338 + _performCaculateAndUpload();
306 // }); 339 // });
307 // result.status = 1; 340 // result.status = 1;
308 // AppToast.show('刷新完成'); 341 // AppToast.show('刷新完成');
309 if (isFromNoPermissionPage) { 342 if (isFromNoPermissionPage) {
310 Get.back(); 343 Get.back();
311 } 344 }
  345 + return true;
312 } else { 346 } else {
313 // Get.to(NoHealthDataPage( 347 // Get.to(NoHealthDataPage(
314 // onRefresh: _performDataUpload, 348 // onRefresh: _performDataUpload,
315 // )); 349 // ));
316 } 350 }
317 - return;  
318 - } else if (result.status == 1) {  
319 - // await LoadingService.instance.run(() async {  
320 - _performHealthDataUpload();  
321 - _startCoreCaculate(refreshAfterComplete: true);  
322 - // }); 351 + return false;
  352 + } else if (_currentStatus == 1) {
  353 + _refreshHealthDataAuthCardStatus(status: 1);
323 if (isFromNoPermissionPage) { 354 if (isFromNoPermissionPage) {
324 Get.back(); 355 Get.back();
325 } 356 }
326 - return; 357 + return true;
327 } 358 }
328 // 打开苹果健康 359 // 打开苹果健康
329 _platformHostApi.nativeHandleUrl('x-apple-health://'); 360 _platformHostApi.nativeHandleUrl('x-apple-health://');
@@ -331,34 +362,54 @@ class TodayController extends GetMaterialController { @@ -331,34 +362,54 @@ class TodayController extends GetMaterialController {
331 AppToast.show(e.toString()); 362 AppToast.show(e.toString());
332 } 363 }
333 } 364 }
  365 + return false;
334 } 366 }
335 367
336 Future<void> onAuthorizeTap() async { 368 Future<void> onAuthorizeTap() async {
  369 + if (_isHandlingHealthAuthorizationTap) {
  370 + AppLogger.d('TodayController.onAuthorizeTap ignored: handling');
  371 + return;
  372 + }
  373 + _isHandlingHealthAuthorizationTap = true;
337 ta.track('click_doublefeel_today_status', properties: {'ita': '授权访问健康数据'}); 374 ta.track('click_doublefeel_today_status', properties: {'ita': '授权访问健康数据'});
338 - // if (showHealthDataAuthCardStatus.value == 1) {  
339 - // return;  
340 - // } else  
341 - if (showHealthDataAuthCardStatus.value == 0) {  
342 - await requestHealthAuthorization(isFromNoPermissionPage: true);  
343 - checkHealthDataAuthCardVisible();  
344 - } else {  
345 - await Get.to(NoHealthDataPage(  
346 - onRefresh: () {  
347 - refreshTab();  
348 - Future.delayed(const Duration(milliseconds: 800), () {  
349 - AppToast.show(l10n.refreshComplete);  
350 - });  
351 - },  
352 - onHelp: () {  
353 - Get.toNamed(Routes.HELP);  
354 - },  
355 - ));  
356 - checkHealthDataAuthCardVisible(); 375 + try {
  376 + if (_currentStatus == 1 && hrvChartData.isNotEmpty) {
  377 + return;
  378 + } else if (_currentStatus == 0) {
  379 + final success = await requestHealthAuthorization();
  380 + if (success) {
  381 + _currentStatus = 1;
  382 + _refreshHealthDataAuthCardStatus(status: 1);
  383 + }
  384 + } else {
  385 + await Get.to(NoHealthDataPage(
  386 + onRefresh: () {
  387 + refreshTab();
  388 + Future.delayed(const Duration(milliseconds: 800), () {
  389 + AppToast.show(l10n.refreshComplete);
  390 + });
  391 + },
  392 + onHelp: () {
  393 + Get.toNamed(Routes.HELP);
  394 + },
  395 + ));
  396 + checkHealthDataAuthCardVisible();
  397 + }
  398 + } finally {
  399 + _isHandlingHealthAuthorizationTap = false;
  400 + }
  401 + }
  402 +
  403 + _refreshHealthDataAuthCardStatus({required int status}) {
  404 + if (status != 1) {
  405 + showHealthDataAuthCardStatus.value = status;
357 } 406 }
  407 + showHealthDataAuthCardStatus.value = hrvChartData.isEmpty ? -1 : status;
358 } 408 }
359 409
360 Future<void> checkHealthDataAuthCardVisible( 410 Future<void> checkHealthDataAuthCardVisible(
361 - {bool isPullToRefresh = false}) async { 411 + {bool isPullToRefresh = false,
  412 + bool shouldCaculateAndUpload = false}) async {
362 final firstTime = DateUtils.dateOnly( 413 final firstTime = DateUtils.dateOnly(
363 DateTime(lastSelectableDay.value.year, lastSelectableDay.value.month - 6, 414 DateTime(lastSelectableDay.value.year, lastSelectableDay.value.month - 6,
364 lastSelectableDay.value.day), 415 lastSelectableDay.value.day),
@@ -368,27 +419,13 @@ class TodayController extends GetMaterialController { @@ -368,27 +419,13 @@ class TodayController extends GetMaterialController {
368 firstSelectableDay.value = firstTime; 419 firstSelectableDay.value = firstTime;
369 } else { 420 } else {
370 try { 421 try {
371 - final result = await _hostApi.checkHealthAppAuthorization();  
372 - ta.setSuperProperties(  
373 - {'health_data_permission': result.status.clamp(0, 1)});  
374 - if (result.status == 1) {  
375 - _calculateHealthDataWithoutLoading(  
376 - uploaded: () async {  
377 - if (DateUtils.isSameDay(  
378 - selectedDate.value, lastSelectableDay.value) &&  
379 - hrvChartData.isEmpty) {  
380 - showHealthDataAuthCardStatus.value = -1;  
381 - } else {  
382 - showHealthDataAuthCardStatus.value = result.status;  
383 - }  
384 - }, 422 + await _checkHealthAuthStatus(
385 isPullToRefresh: isPullToRefresh, 423 isPullToRefresh: isPullToRefresh,
386 - );  
387 - } else {  
388 - unawaited(loadDataForDate(selectedDate.value,  
389 - isPullToRefresh: isPullToRefresh));  
390 - showHealthDataAuthCardStatus.value = result.status;  
391 - } 424 + shouldCaculateAndUpload: shouldCaculateAndUpload);
  425 + ta.setSuperProperties(
  426 + {'health_data_permission': _currentStatus.clamp(0, 1)});
  427 + unawaited(loadDataForDate(selectedDate.value,
  428 + isPullToRefresh: isPullToRefresh));
392 } catch (error, stackTrace) { 429 } catch (error, stackTrace) {
393 AppLogger.e( 430 AppLogger.e(
394 'TodayController.checkHealthDataAuthCardVisible failed', 431 'TodayController.checkHealthDataAuthCardVisible failed',
@@ -415,11 +452,13 @@ class TodayController extends GetMaterialController { @@ -415,11 +452,13 @@ class TodayController extends GetMaterialController {
415 } 452 }
416 case AppFailure(): 453 case AppFailure():
417 } 454 }
418 - showHealthDataAuthCardStatus.value = !hasUploaded ? -1 : result.status; 455 + _refreshHealthDataAuthCardStatus(status: result.status);
419 } 456 }
420 457
421 void _calculateHealthDataWithoutLoading( 458 void _calculateHealthDataWithoutLoading(
422 - {Future<void> Function()? uploaded, bool isPullToRefresh = false}) { 459 + {Future<void> Function()? uploaded,
  460 + bool isPullToRefresh = false,
  461 + bool refreshAfterComplete = true}) {
423 unawaited( 462 unawaited(
424 (() async { 463 (() async {
425 try { 464 try {
@@ -427,6 +466,9 @@ class TodayController extends GetMaterialController { @@ -427,6 +466,9 @@ class TodayController extends GetMaterialController {
427 await loadDataForDate(selectedDate.value, 466 await loadDataForDate(selectedDate.value,
428 isPullToRefresh: isPullToRefresh); 467 isPullToRefresh: isPullToRefresh);
429 await uploaded?.call(); 468 await uploaded?.call();
  469 + if (refreshAfterComplete) {
  470 + await loadDataForDate(selectedDate.value);
  471 + }
430 } catch (error, stackTrace) { 472 } catch (error, stackTrace) {
431 AppLogger.e('Apple Health calculate failed', error, stackTrace); 473 AppLogger.e('Apple Health calculate failed', error, stackTrace);
432 } 474 }
@@ -749,7 +791,7 @@ class TodayController extends GetMaterialController { @@ -749,7 +791,7 @@ class TodayController extends GetMaterialController {
749 changeDate(today); 791 changeDate(today);
750 checkAddFriendVisible(); 792 checkAddFriendVisible();
751 checkHrvAdBannerVisible(); 793 checkHrvAdBannerVisible();
752 - checkHealthDataAuthCardVisible(); 794 + checkHealthDataAuthCardVisible(shouldCaculateAndUpload: true);
753 checkNotificationAuthorizationBannerVisible(); 795 checkNotificationAuthorizationBannerVisible();
754 _performHealthDataUpload(); 796 _performHealthDataUpload();
755 // _startCoreCaculate(); 797 // _startCoreCaculate();
@@ -789,7 +831,7 @@ class TodayController extends GetMaterialController { @@ -789,7 +831,7 @@ class TodayController extends GetMaterialController {
789 } 831 }
790 832
791 // 未决定 → 弹出系统授权弹窗 833 // 未决定 → 弹出系统授权弹窗
792 - final result = await _platformHostApi.requestNotificationAuth(); 834 + await _platformHostApi.requestNotificationAuth();
793 // AppLogger.d(result.isGranted 835 // AppLogger.d(result.isGranted
794 // ? 'Notification authorization granted' 836 // ? 'Notification authorization granted'
795 // : 'Notification authorization skipped'); 837 // : 'Notification authorization skipped');