Showing
1 changed file
with
100 additions
and
58 deletions
| @@ -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'); |
-
Please register or login to post a comment