Showing
35 changed files
with
284 additions
and
92 deletions
| 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/app/utils/platform_compact.dart'; | ||
| 2 | import 'package:doublefeel_flutter/core/theme/app_colors.dart'; | 3 | import 'package:doublefeel_flutter/core/theme/app_colors.dart'; |
| 3 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 4 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 4 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; | 5 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; |
| 5 | - | ||
| 6 | import 'package:flutter/material.dart'; | 6 | import 'package:flutter/material.dart'; |
| 7 | import 'package:get/get.dart'; | 7 | import 'package:get/get.dart'; |
| 8 | 8 | ||
| @@ -69,10 +69,33 @@ class HelpView extends GetView<HelpController> { | @@ -69,10 +69,33 @@ class HelpView extends GetView<HelpController> { | ||
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | List<({String title, List<String> body})> _buildFaqItems( | 71 | List<({String title, List<String> body})> _buildFaqItems( |
| 72 | - BuildContext context, | ||
| 73 | - ) { | 72 | + BuildContext context) { |
| 74 | final l10n = context.l10n; | 73 | final l10n = context.l10n; |
| 75 | 74 | ||
| 75 | + if (isOhos()) { | ||
| 76 | + return [ | ||
| 77 | + ( | ||
| 78 | + title: "APP没有数据怎么办", | ||
| 79 | + body: [ | ||
| 80 | + "1、 确定手表系统在 5.1.0.32SP3 及以上,手表型号需为 WATCH GT 5 系列(含 Pro)/ WATCH 4 系列以上,手环从华为手环 10 及以上。", | ||
| 81 | + "2、确认设备是否处于省电模式、低电量状态或手表佩戴未贴紧,以上情况会影响手表数据采集。", | ||
| 82 | + l10n.helpNoDataReasonFooter, | ||
| 83 | + ], | ||
| 84 | + ), | ||
| 85 | + ( | ||
| 86 | + title: l10n.todayFaqLinkHrvRealtimeUpdate, | ||
| 87 | + body: [ | ||
| 88 | + "在智能手表的默认设置下,HRV 每1小时更新一次。", | ||
| 89 | + "为了解决 HRV 更新间隔较长的问题,DoubleFeel 设计了实时压力功能:", | ||
| 90 | + "实时压力每6分钟更新一次(好友状态更新依赖 华为健康 同步,受系统机制影响可能存在短暂延迟。若好友经常使用DoubleFeel,健康状态会更新得更加及时)", | ||
| 91 | + l10n.todayRealtimeStressScenarioTimely, | ||
| 92 | + l10n.todayRealtimeStressScenarioConsistentTrend, | ||
| 93 | + l10n.todayRealtimeStressScenarioSummary, | ||
| 94 | + ], | ||
| 95 | + ) | ||
| 96 | + ]; | ||
| 97 | + } | ||
| 98 | + | ||
| 76 | return [ | 99 | return [ |
| 77 | ( | 100 | ( |
| 78 | title: l10n.todayFaqLinkNoData, | 101 | title: l10n.todayFaqLinkNoData, |
| @@ -141,7 +164,7 @@ class _FaqCard extends StatelessWidget { | @@ -141,7 +164,7 @@ class _FaqCard extends StatelessWidget { | ||
| 141 | @override | 164 | @override |
| 142 | Widget build(BuildContext context) { | 165 | Widget build(BuildContext context) { |
| 143 | return Container( | 166 | return Container( |
| 144 | - height: 257, | 167 | + height: 149, |
| 145 | padding: EdgeInsets.fromLTRB(20, 20, 20, 9), | 168 | padding: EdgeInsets.fromLTRB(20, 20, 20, 9), |
| 146 | decoration: BoxDecoration( | 169 | decoration: BoxDecoration( |
| 147 | color: Colors.white, | 170 | color: Colors.white, |
| @@ -305,26 +305,26 @@ class TodayController extends GetxController with WidgetsBindingObserver { | @@ -305,26 +305,26 @@ class TodayController extends GetxController with WidgetsBindingObserver { | ||
| 305 | } | 305 | } |
| 306 | } | 306 | } |
| 307 | 307 | ||
| 308 | - Future<void> _checkHealthAuthStatus( | 308 | + Future<bool> _checkHealthAuthStatus( |
| 309 | {bool isPullToRefresh = false, | 309 | {bool isPullToRefresh = false, |
| 310 | bool shouldCaculateAndUpload = false}) async { | 310 | bool shouldCaculateAndUpload = false}) async { |
| 311 | final result = await _hostApi.checkHealthAppAuthorization(); | 311 | final result = await _hostApi.checkHealthAppAuthorization(); |
| 312 | _currentStatus = result.status; | 312 | _currentStatus = result.status; |
| 313 | _refreshHealthDataAuthCardStatus(status: _currentStatus); | 313 | _refreshHealthDataAuthCardStatus(status: _currentStatus); |
| 314 | if (_currentStatus != 1) { | 314 | if (_currentStatus != 1) { |
| 315 | - return; | 315 | + return false; |
| 316 | } | 316 | } |
| 317 | if (!shouldCaculateAndUpload) { | 317 | if (!shouldCaculateAndUpload) { |
| 318 | - return; | 318 | + return false; |
| 319 | } | 319 | } |
| 320 | - _performCaculateAndUpload(isPullToRefresh: isPullToRefresh); | 320 | + return _performCaculateAndUpload(isPullToRefresh: isPullToRefresh); |
| 321 | } | 321 | } |
| 322 | 322 | ||
| 323 | - _performCaculateAndUpload({ | 323 | + Future<bool> _performCaculateAndUpload({ |
| 324 | bool isPullToRefresh = false, | 324 | bool isPullToRefresh = false, |
| 325 | }) { | 325 | }) { |
| 326 | _performHealthDataUpload(); | 326 | _performHealthDataUpload(); |
| 327 | - _calculateHealthDataWithoutLoading( | 327 | + return _calculateHealthDataWithoutLoading( |
| 328 | uploaded: () async { | 328 | uploaded: () async { |
| 329 | _refreshHealthDataAuthCardStatus(status: 1); | 329 | _refreshHealthDataAuthCardStatus(status: 1); |
| 330 | }, | 330 | }, |
| @@ -520,13 +520,21 @@ class TodayController extends GetxController with WidgetsBindingObserver { | @@ -520,13 +520,21 @@ class TodayController extends GetxController with WidgetsBindingObserver { | ||
| 520 | firstSelectableDay.value = firstTime; | 520 | firstSelectableDay.value = firstTime; |
| 521 | } else { | 521 | } else { |
| 522 | try { | 522 | try { |
| 523 | - await _checkHealthAuthStatus( | 523 | + final didCalculateAndReload = await _checkHealthAuthStatus( |
| 524 | isPullToRefresh: isPullToRefresh, | 524 | isPullToRefresh: isPullToRefresh, |
| 525 | shouldCaculateAndUpload: shouldCaculateAndUpload); | 525 | shouldCaculateAndUpload: shouldCaculateAndUpload); |
| 526 | ta.setSuperProperties( | 526 | ta.setSuperProperties( |
| 527 | {'health_data_permission': _currentStatus.clamp(0, 1)}); | 527 | {'health_data_permission': _currentStatus.clamp(0, 1)}); |
| 528 | - unawaited(loadDataForDate(selectedDate.value, | ||
| 529 | - isPullToRefresh: isPullToRefresh)); | 528 | + // When a pull-to-refresh asks OHOS to recalculate health data, |
| 529 | + // _calculateHealthDataWithoutLoading already reloads the page after | ||
| 530 | + // the calculation. Starting another load here caused a second visible | ||
| 531 | + // refresh shortly after the first one completed. | ||
| 532 | + if (!didCalculateAndReload) { | ||
| 533 | + await loadDataForDate( | ||
| 534 | + selectedDate.value, | ||
| 535 | + isPullToRefresh: isPullToRefresh, | ||
| 536 | + ); | ||
| 537 | + } | ||
| 530 | } catch (error, stackTrace) { | 538 | } catch (error, stackTrace) { |
| 531 | AppLogger.e( | 539 | AppLogger.e( |
| 532 | 'TodayController.checkHealthDataAuthCardVisible failed', | 540 | 'TodayController.checkHealthDataAuthCardVisible failed', |
| @@ -556,25 +564,25 @@ class TodayController extends GetxController with WidgetsBindingObserver { | @@ -556,25 +564,25 @@ class TodayController extends GetxController with WidgetsBindingObserver { | ||
| 556 | _refreshHealthDataAuthCardStatus(status: result.status); | 564 | _refreshHealthDataAuthCardStatus(status: result.status); |
| 557 | } | 565 | } |
| 558 | 566 | ||
| 559 | - void _calculateHealthDataWithoutLoading( | 567 | + Future<bool> _calculateHealthDataWithoutLoading( |
| 560 | {Future<void> Function()? uploaded, | 568 | {Future<void> Function()? uploaded, |
| 561 | bool isPullToRefresh = false, | 569 | bool isPullToRefresh = false, |
| 562 | - bool refreshAfterComplete = false}) { | ||
| 563 | - unawaited( | ||
| 564 | - (() async { | ||
| 565 | - try { | ||
| 566 | - await _healthRawDataCoreService.startCoreCaculate(); | ||
| 567 | - await loadDataForDate(selectedDate.value, | ||
| 568 | - isPullToRefresh: isPullToRefresh); | ||
| 569 | - await uploaded?.call(); | ||
| 570 | - if (refreshAfterComplete) { | ||
| 571 | - await loadDataForDate(selectedDate.value); | ||
| 572 | - } | ||
| 573 | - } catch (error, stackTrace) { | ||
| 574 | - AppLogger.e('Apple Health calculate failed', error, stackTrace); | ||
| 575 | - } | ||
| 576 | - })(), | ||
| 577 | - ); | 570 | + bool refreshAfterComplete = false}) async { |
| 571 | + try { | ||
| 572 | + await _healthRawDataCoreService.startCoreCaculate(); | ||
| 573 | + await loadDataForDate( | ||
| 574 | + selectedDate.value, | ||
| 575 | + isPullToRefresh: isPullToRefresh, | ||
| 576 | + ); | ||
| 577 | + await uploaded?.call(); | ||
| 578 | + if (refreshAfterComplete) { | ||
| 579 | + await loadDataForDate(selectedDate.value); | ||
| 580 | + } | ||
| 581 | + return true; | ||
| 582 | + } catch (error, stackTrace) { | ||
| 583 | + AppLogger.e('Apple Health calculate failed', error, stackTrace); | ||
| 584 | + return false; | ||
| 585 | + } | ||
| 578 | } | 586 | } |
| 579 | 587 | ||
| 580 | void _performHealthDataUpload() { | 588 | void _performHealthDataUpload() { |
| @@ -925,7 +933,7 @@ class TodayController extends GetxController with WidgetsBindingObserver { | @@ -925,7 +933,7 @@ class TodayController extends GetxController with WidgetsBindingObserver { | ||
| 925 | DateUtils.dateOnly(selectedDate.value), | 933 | DateUtils.dateOnly(selectedDate.value), |
| 926 | DateUtils.dateOnly(DateTime.now()), | 934 | DateUtils.dateOnly(DateTime.now()), |
| 927 | ); | 935 | ); |
| 928 | - checkHealthDataAuthCardVisible( | 936 | + await checkHealthDataAuthCardVisible( |
| 929 | isPullToRefresh: isPullToRefresh, | 937 | isPullToRefresh: isPullToRefresh, |
| 930 | shouldCaculateAndUpload: isOhos() && isSelectedToday, | 938 | shouldCaculateAndUpload: isOhos() && isSelectedToday, |
| 931 | ); | 939 | ); |
| @@ -3,20 +3,17 @@ import 'package:doublefeel_flutter/app/models/dialog_meta_data.dart'; | @@ -3,20 +3,17 @@ import 'package:doublefeel_flutter/app/models/dialog_meta_data.dart'; | ||
| 3 | import 'package:doublefeel_flutter/app/modules/home/controllers/my_controller.dart'; | 3 | import 'package:doublefeel_flutter/app/modules/home/controllers/my_controller.dart'; |
| 4 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | 4 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
| 5 | import 'package:doublefeel_flutter/app/utils/dialog_utils.dart'; | 5 | import 'package:doublefeel_flutter/app/utils/dialog_utils.dart'; |
| 6 | -import 'package:doublefeel_flutter/app/widget/marquee_text.dart'; | ||
| 7 | - | 6 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; |
| 8 | import 'package:doublefeel_flutter/core/network/api/user_api.dart'; | 7 | import 'package:doublefeel_flutter/core/network/api/user_api.dart'; |
| 9 | import 'package:doublefeel_flutter/core/result/app_result.dart'; | 8 | import 'package:doublefeel_flutter/core/result/app_result.dart'; |
| 10 | import 'package:doublefeel_flutter/core/services/loading_service.dart'; | 9 | import 'package:doublefeel_flutter/core/services/loading_service.dart'; |
| 11 | import 'package:doublefeel_flutter/core/services/user_state_service.dart'; | 10 | import 'package:doublefeel_flutter/core/services/user_state_service.dart'; |
| 12 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 11 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 13 | import 'package:doublefeel_flutter/core/util/app_toast.dart'; | 12 | import 'package:doublefeel_flutter/core/util/app_toast.dart'; |
| 14 | - | ||
| 15 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; | 13 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; |
| 16 | import 'package:doublefeel_flutter/data/models/user/user_models.dart'; | 14 | import 'package:doublefeel_flutter/data/models/user/user_models.dart'; |
| 17 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; | 15 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; |
| 18 | import 'package:flutter/material.dart'; | 16 | import 'package:flutter/material.dart'; |
| 19 | - | ||
| 20 | import 'package:get/get.dart'; | 17 | import 'package:get/get.dart'; |
| 21 | 18 | ||
| 22 | class AccountSettingView extends GetView<MyController> { | 19 | class AccountSettingView extends GetView<MyController> { |
| @@ -496,7 +493,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget { | @@ -496,7 +493,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget { | ||
| 496 | height: 12, | 493 | height: 12, |
| 497 | ), | 494 | ), |
| 498 | Text( | 495 | Text( |
| 499 | - context.l10n.deleteAccountWarningNote1, | 496 | + _getWarningNote1(context), |
| 500 | style: TextStyle( | 497 | style: TextStyle( |
| 501 | color: context.colors.textSecondary, | 498 | color: context.colors.textSecondary, |
| 502 | fontSize: 12, | 499 | fontSize: 12, |
| @@ -507,7 +504,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget { | @@ -507,7 +504,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget { | ||
| 507 | height: 12, | 504 | height: 12, |
| 508 | ), | 505 | ), |
| 509 | Text( | 506 | Text( |
| 510 | - context.l10n.deleteAccountWarningNote2, | 507 | + _getWarningNote2(context), |
| 511 | style: TextStyle( | 508 | style: TextStyle( |
| 512 | color: context.colors.textSecondary, | 509 | color: context.colors.textSecondary, |
| 513 | fontSize: 12, | 510 | fontSize: 12, |
| @@ -570,4 +567,18 @@ class _DeleteAccountBottomSheet extends StatelessWidget { | @@ -570,4 +567,18 @@ class _DeleteAccountBottomSheet extends StatelessWidget { | ||
| 570 | ), | 567 | ), |
| 571 | ); | 568 | ); |
| 572 | } | 569 | } |
| 570 | + | ||
| 571 | + String _getWarningNote1(BuildContext context) { | ||
| 572 | + if (isOhos()) { | ||
| 573 | + return "注1:你的健康数据会保存在华为健康,我们不会删除华为健康中的数据。"; | ||
| 574 | + } | ||
| 575 | + return context.l10n.deleteAccountWarningNote1; | ||
| 576 | + } | ||
| 577 | + | ||
| 578 | + String _getWarningNote2(BuildContext context) { | ||
| 579 | + if (isOhos()) { | ||
| 580 | + return "注2:删除账号不会影响你在支付宝的自动续费状态,如果需要取消自动续费,请在支付宝中手动取消。"; | ||
| 581 | + } | ||
| 582 | + return context.l10n.deleteAccountWarningNote2; | ||
| 583 | + } | ||
| 573 | } | 584 | } |
| @@ -150,7 +150,7 @@ class StressStatusExplanationBottomSheet extends StatelessWidget { | @@ -150,7 +150,7 @@ class StressStatusExplanationBottomSheet extends StatelessWidget { | ||
| 150 | const SizedBox(height: 12), | 150 | const SizedBox(height: 12), |
| 151 | isOhos() | 151 | isOhos() |
| 152 | ? _InfoCard( | 152 | ? _InfoCard( |
| 153 | - title: '手机 App 与智能师表显示的压力状态有什么区别?', | 153 | + title: '手机 App 与智能手表显示的压力状态有什么区别?', |
| 154 | children: [ | 154 | children: [ |
| 155 | _InfoText( | 155 | _InfoText( |
| 156 | '手机 App 首页显示的是当天的综合压力状态,会综合分析 HRV、静息心率与整日身体趋势。'), | 156 | '手机 App 首页显示的是当天的综合压力状态,会综合分析 HRV、静息心率与整日身体趋势。'), |
| @@ -46,7 +46,9 @@ class TodayHealthDataAuthCard extends StatelessWidget { | @@ -46,7 +46,9 @@ class TodayHealthDataAuthCard extends StatelessWidget { | ||
| 46 | ), | 46 | ), |
| 47 | const SizedBox(height: 5), | 47 | const SizedBox(height: 5), |
| 48 | Text( | 48 | Text( |
| 49 | - l10n.todayHealthDataAuthDescription, | 49 | + isOhos() |
| 50 | + ? 'DoubleFeel 需要访问你的华为健康数据,以提供压力提醒、实时压力统计和健康建议。\n如果尚未授权,请点击下方完成授权;如果已经授权,首次同步大量健康数据可能需要等待几分钟,请稍后再试。' | ||
| 51 | + : l10n.todayHealthDataAuthDescription, | ||
| 50 | style: TextStyle( | 52 | style: TextStyle( |
| 51 | color: colors.textSecondary, | 53 | color: colors.textSecondary, |
| 52 | fontSize: 13, | 54 | fontSize: 13, |
| 1 | import 'dart:async'; | 1 | import 'dart:async'; |
| 2 | 2 | ||
| 3 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 3 | import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; | 4 | import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; |
| 4 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 5 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 5 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; | 6 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; |
| @@ -9,7 +10,6 @@ import 'package:fl_chart/fl_chart.dart'; | @@ -9,7 +10,6 @@ import 'package:fl_chart/fl_chart.dart'; | ||
| 9 | import 'package:flutter/material.dart'; | 10 | import 'package:flutter/material.dart'; |
| 10 | import 'package:get/get.dart'; | 11 | import 'package:get/get.dart'; |
| 11 | import 'package:intl/intl.dart'; | 12 | import 'package:intl/intl.dart'; |
| 12 | -import 'package:lottie/lottie.dart'; | ||
| 13 | 13 | ||
| 14 | import '../../controllers/today_controller.dart'; | 14 | import '../../controllers/today_controller.dart'; |
| 15 | import 'hrv_principle_explanation_bottom_sheet.dart'; | 15 | import 'hrv_principle_explanation_bottom_sheet.dart'; |
| @@ -399,6 +399,7 @@ class TodayHrvChartCard extends StatelessWidget { | @@ -399,6 +399,7 @@ class TodayHrvChartCard extends StatelessWidget { | ||
| 399 | 399 | ||
| 400 | List<TextSpan>? _getTooltip( | 400 | List<TextSpan>? _getTooltip( |
| 401 | BarChartRodData rod, int x, int? state, bool? isWorkout) { | 401 | BarChartRodData rod, int x, int? state, bool? isWorkout) { |
| 402 | + final useOhosIcon = isOhos(); | ||
| 402 | return [ | 403 | return [ |
| 403 | TextSpan( | 404 | TextSpan( |
| 404 | text: isWorkout == true ? '◉ ' : '○ ', | 405 | text: isWorkout == true ? '◉ ' : '○ ', |
| @@ -432,14 +433,16 @@ class TodayHrvChartCard extends StatelessWidget { | @@ -432,14 +433,16 @@ class TodayHrvChartCard extends StatelessWidget { | ||
| 432 | ), | 433 | ), |
| 433 | ), | 434 | ), |
| 434 | TextSpan( | 435 | TextSpan( |
| 435 | - text: '♥ ', | ||
| 436 | - style: const TextStyle( | ||
| 437 | - color: Color(0xFFB0B0B6), | ||
| 438 | - fontSize: 14, | ||
| 439 | - fontWeight: FontWeight.bold, | 436 | + text: |
| 437 | + useOhosIcon ? String.fromCharCode(Icons.favorite.codePoint) : '♥ ', | ||
| 438 | + style: TextStyle( | ||
| 440 | inherit: false, | 439 | inherit: false, |
| 441 | fontFamilyFallback: null, | 440 | fontFamilyFallback: null, |
| 442 | - fontFamily: '', | 441 | + fontFamily: useOhosIcon ? Icons.favorite.fontFamily : '', |
| 442 | + package: useOhosIcon ? Icons.favorite.fontPackage : null, | ||
| 443 | + color: const Color(0xFFB0B0B6), | ||
| 444 | + fontSize: 14, | ||
| 445 | + fontWeight: FontWeight.bold, | ||
| 443 | ), | 446 | ), |
| 444 | ), | 447 | ), |
| 445 | TextSpan( | 448 | TextSpan( |
| @@ -454,6 +457,7 @@ class TodayHrvChartCard extends StatelessWidget { | @@ -454,6 +457,7 @@ class TodayHrvChartCard extends StatelessWidget { | ||
| 454 | } | 457 | } |
| 455 | 458 | ||
| 456 | List<TextSpan>? _getTooltipChildren(LineBarSpot flSpot, int? state) { | 459 | List<TextSpan>? _getTooltipChildren(LineBarSpot flSpot, int? state) { |
| 460 | + final useOhosIcon = isOhos(); | ||
| 457 | return [ | 461 | return [ |
| 458 | TextSpan( | 462 | TextSpan( |
| 459 | text: '○ ', | 463 | text: '○ ', |
| @@ -482,12 +486,14 @@ class TodayHrvChartCard extends StatelessWidget { | @@ -482,12 +486,14 @@ class TodayHrvChartCard extends StatelessWidget { | ||
| 482 | ), | 486 | ), |
| 483 | ), | 487 | ), |
| 484 | TextSpan( | 488 | TextSpan( |
| 485 | - text: '♥ ', | ||
| 486 | - style: const TextStyle( | 489 | + text: |
| 490 | + useOhosIcon ? String.fromCharCode(Icons.favorite.codePoint) : '♥ ', | ||
| 491 | + style: TextStyle( | ||
| 487 | inherit: false, | 492 | inherit: false, |
| 488 | fontFamilyFallback: null, | 493 | fontFamilyFallback: null, |
| 489 | - fontFamily: '', | ||
| 490 | - color: Color(0xFFB0B0B6), | 494 | + fontFamily: useOhosIcon ? Icons.favorite.fontFamily : '', |
| 495 | + package: useOhosIcon ? Icons.favorite.fontPackage : null, | ||
| 496 | + color: const Color(0xFFB0B0B6), | ||
| 491 | fontSize: 14, | 497 | fontSize: 14, |
| 492 | fontWeight: FontWeight.bold, | 498 | fontWeight: FontWeight.bold, |
| 493 | ), | 499 | ), |
| @@ -962,10 +968,13 @@ class _StressBarChartState extends State<_StressBarChart> { | @@ -962,10 +968,13 @@ class _StressBarChartState extends State<_StressBarChart> { | ||
| 962 | final centerOfBar = | 968 | final centerOfBar = |
| 963 | _stressTouchedGroupIndex! * stepWidth + (barWidth / 2); | 969 | _stressTouchedGroupIndex! * stepWidth + (barWidth / 2); |
| 964 | customIndicator = Positioned( | 970 | customIndicator = Positioned( |
| 965 | - left: centerOfBar - 1.0, // 2px 宽度,居中 | 971 | + left: centerOfBar - 1.0, |
| 972 | + // 2px 宽度,居中 | ||
| 966 | width: 2.0, | 973 | width: 2.0, |
| 967 | - top: 12.0, // 向上延展以连接 tooltip 底部 | ||
| 968 | - bottom: chartHeight - gridBottom, // 修正:bottom 指的是距离 Stack 底部的像素值 | 974 | + top: 12.0, |
| 975 | + // 向上延展以连接 tooltip 底部 | ||
| 976 | + bottom: chartHeight - gridBottom, | ||
| 977 | + // 修正:bottom 指的是距离 Stack 底部的像素值 | ||
| 969 | child: IgnorePointer( | 978 | child: IgnorePointer( |
| 970 | child: ColoredBox( | 979 | child: ColoredBox( |
| 971 | color: context.colors.textTertiary, | 980 | color: context.colors.textTertiary, |
| @@ -1144,9 +1153,10 @@ class _StressBarChartState extends State<_StressBarChart> { | @@ -1144,9 +1153,10 @@ class _StressBarChartState extends State<_StressBarChart> { | ||
| 1144 | tooltipBorder: BorderSide.none, | 1153 | tooltipBorder: BorderSide.none, |
| 1145 | maxContentWidth: 400, | 1154 | maxContentWidth: 400, |
| 1146 | fitInsideHorizontally: true, | 1155 | fitInsideHorizontally: true, |
| 1147 | - fitInsideVertically: | ||
| 1148 | - false, // 允许 tooltip 超出 BarChart 顶边界(绘制在 18px 的 top margin 区域内) | ||
| 1149 | - tooltipMargin: 6, // 调整为和折线图一样的 6 像素空隙 | 1156 | + fitInsideVertically: false, |
| 1157 | + // 允许 tooltip 超出 BarChart 顶边界(绘制在 18px 的 top margin 区域内) | ||
| 1158 | + tooltipMargin: 6, | ||
| 1159 | + // 调整为和折线图一样的 6 像素空隙 | ||
| 1150 | getTooltipColor: (touchedSpot) => const Color(0xFFF3F3F3), | 1160 | getTooltipColor: (touchedSpot) => const Color(0xFFF3F3F3), |
| 1151 | tooltipPadding: const EdgeInsets.only( | 1161 | tooltipPadding: const EdgeInsets.only( |
| 1152 | left: 12, | 1162 | left: 12, |
| 1 | -import 'package:cached_network_image/cached_network_image.dart'; | ||
| 2 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 1 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 3 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; | 2 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; |
| 4 | import 'package:doublefeel_flutter/data/models/friend/friend_models.dart'; | 3 | import 'package:doublefeel_flutter/data/models/friend/friend_models.dart'; |
| @@ -40,7 +39,7 @@ class TrendFriendSelectBottomSheet extends StatelessWidget { | @@ -40,7 +39,7 @@ class TrendFriendSelectBottomSheet extends StatelessWidget { | ||
| 40 | children: [ | 39 | children: [ |
| 41 | Center( | 40 | Center( |
| 42 | child: Text( | 41 | child: Text( |
| 43 | - 'Choose a Loved One', | 42 | + context.l10n.friendsSelect, |
| 44 | style: TextStyle( | 43 | style: TextStyle( |
| 45 | color: context.colors.textPrimary, | 44 | color: context.colors.textPrimary, |
| 46 | fontSize: 16, | 45 | fontSize: 16, |
| 1 | import 'dart:async'; | 1 | import 'dart:async'; |
| 2 | import 'dart:convert'; | 2 | import 'dart:convert'; |
| 3 | + | ||
| 3 | import 'package:crypto/crypto.dart'; | 4 | import 'package:crypto/crypto.dart'; |
| 4 | -import 'package:flutter_timezone/flutter_timezone.dart'; | ||
| 5 | import 'package:doublefeel_flutter/app/modules/login/views/email_login_new_password_view.dart'; | 5 | import 'package:doublefeel_flutter/app/modules/login/views/email_login_new_password_view.dart'; |
| 6 | import 'package:doublefeel_flutter/app/modules/login/views/email_login_view.dart'; | 6 | import 'package:doublefeel_flutter/app/modules/login/views/email_login_view.dart'; |
| 7 | +import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | ||
| 7 | import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; | 8 | import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; |
| 8 | import 'package:doublefeel_flutter/core/constants/app_const.dart'; | 9 | import 'package:doublefeel_flutter/core/constants/app_const.dart'; |
| 9 | import 'package:doublefeel_flutter/core/logging/app_logger.dart'; | 10 | import 'package:doublefeel_flutter/core/logging/app_logger.dart'; |
| 10 | -import 'package:doublefeel_flutter/core/services/app_config_service.dart'; | ||
| 11 | -import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; | ||
| 12 | -import 'package:doublefeel_flutter/data/models/local/user_preferences.dart'; | ||
| 13 | -import 'package:doublefeel_flutter/l10n/gen/app_localizations.dart'; | ||
| 14 | -import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; | ||
| 15 | -import 'package:doublefeel_flutter/core/platform/pigeon_api_facade.dart'; | ||
| 16 | -import 'package:flutter/services.dart'; | ||
| 17 | -import 'package:flutter/widgets.dart'; | ||
| 18 | -import 'package:get/get.dart'; | ||
| 19 | -import 'package:doublefeel_flutter/core/util/app_toast.dart'; | ||
| 20 | import 'package:doublefeel_flutter/core/network/api/user_api.dart'; | 11 | import 'package:doublefeel_flutter/core/network/api/user_api.dart'; |
| 21 | import 'package:doublefeel_flutter/core/network/api/vip_api.dart'; | 12 | import 'package:doublefeel_flutter/core/network/api/vip_api.dart'; |
| 13 | +import 'package:doublefeel_flutter/core/platform/pigeon_api_facade.dart'; | ||
| 14 | +import 'package:doublefeel_flutter/core/result/app_result.dart'; | ||
| 15 | +import 'package:doublefeel_flutter/core/services/app_config_service.dart'; | ||
| 16 | +import 'package:doublefeel_flutter/core/services/loading_service.dart'; | ||
| 17 | +import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; | ||
| 22 | import 'package:doublefeel_flutter/core/services/user_state_service.dart'; | 18 | import 'package:doublefeel_flutter/core/services/user_state_service.dart'; |
| 19 | +import 'package:doublefeel_flutter/core/util/app_toast.dart'; | ||
| 23 | import 'package:doublefeel_flutter/data/local/local_storage.dart'; | 20 | import 'package:doublefeel_flutter/data/local/local_storage.dart'; |
| 24 | import 'package:doublefeel_flutter/data/local/user_account_storage.dart'; | 21 | import 'package:doublefeel_flutter/data/local/user_account_storage.dart'; |
| 25 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; | 22 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; |
| 23 | +import 'package:doublefeel_flutter/data/models/local/user_preferences.dart'; | ||
| 26 | import 'package:doublefeel_flutter/data/models/user/user_models.dart'; | 24 | import 'package:doublefeel_flutter/data/models/user/user_models.dart'; |
| 27 | import 'package:doublefeel_flutter/data/models/vip/vip_info.dart'; | 25 | import 'package:doublefeel_flutter/data/models/vip/vip_info.dart'; |
| 28 | -import 'package:doublefeel_flutter/core/result/app_result.dart'; | ||
| 29 | -import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | ||
| 30 | -import 'package:doublefeel_flutter/core/services/loading_service.dart'; | 26 | +import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; |
| 31 | import 'package:doublefeel_flutter/pigeon/wechat_api.g.dart'; | 27 | import 'package:doublefeel_flutter/pigeon/wechat_api.g.dart'; |
| 28 | +import 'package:flutter/services.dart'; | ||
| 29 | +import 'package:flutter/widgets.dart'; | ||
| 30 | +import 'package:flutter_timezone/flutter_timezone.dart'; | ||
| 31 | +import 'package:get/get.dart'; | ||
| 32 | 32 | ||
| 33 | class LoginController extends GetxController { | 33 | class LoginController extends GetxController { |
| 34 | final UserApi _userApi = Get.find<UserApi>(); | 34 | final UserApi _userApi = Get.find<UserApi>(); |
| @@ -81,6 +81,7 @@ class LoginController extends GetxController { | @@ -81,6 +81,7 @@ class LoginController extends GetxController { | ||
| 81 | bool get isEmailValid => | 81 | bool get isEmailValid => |
| 82 | _emailRegex.hasMatch(emailInput.value.trim()) && | 82 | _emailRegex.hasMatch(emailInput.value.trim()) && |
| 83 | emailInput.value.isNotEmpty; | 83 | emailInput.value.isNotEmpty; |
| 84 | + | ||
| 84 | bool get isPasswordValid => | 85 | bool get isPasswordValid => |
| 85 | _passwordRegex.hasMatch(passwordInput.value) && | 86 | _passwordRegex.hasMatch(passwordInput.value) && |
| 86 | passwordInput.value.isNotEmpty; | 87 | passwordInput.value.isNotEmpty; |
| @@ -205,7 +206,16 @@ class LoginController extends GetxController { | @@ -205,7 +206,16 @@ class LoginController extends GetxController { | ||
| 205 | if (code.isEmpty) return; | 206 | if (code.isEmpty) return; |
| 206 | await _loginWithWechat(code); | 207 | await _loginWithWechat(code); |
| 207 | } catch (e) { | 208 | } catch (e) { |
| 208 | - AppToast.show(e.toString()); | 209 | + if (e is PlatformException) { |
| 210 | + var msg = e.message ?? ""; | ||
| 211 | + if (msg.isNotEmpty) { | ||
| 212 | + AppToast.show(msg); | ||
| 213 | + } else { | ||
| 214 | + AppToast.show("微信登录失败"); | ||
| 215 | + } | ||
| 216 | + } else { | ||
| 217 | + AppToast.show(e.toString()); | ||
| 218 | + } | ||
| 209 | } | 219 | } |
| 210 | } | 220 | } |
| 211 | 221 | ||
| @@ -556,7 +566,7 @@ class LoginController extends GetxController { | @@ -556,7 +566,7 @@ class LoginController extends GetxController { | ||
| 556 | } else if (isWechat) { | 566 | } else if (isWechat) { |
| 557 | registerData = { | 567 | registerData = { |
| 558 | 'login_type': 'wechat', | 568 | 'login_type': 'wechat', |
| 559 | - 'code': wechatCode, | 569 | + 'wechat_code': wechatCode, |
| 560 | }; | 570 | }; |
| 561 | } else { | 571 | } else { |
| 562 | registerData = { | 572 | registerData = { |
| @@ -2,6 +2,7 @@ import 'package:doublefeel_flutter/app/modules/home/views/tabs/my_tab.dart'; | @@ -2,6 +2,7 @@ import 'package:doublefeel_flutter/app/modules/home/views/tabs/my_tab.dart'; | ||
| 2 | import 'package:doublefeel_flutter/app/modules/membership_offer/controllers/membership_detail_controller.dart'; | 2 | import 'package:doublefeel_flutter/app/modules/membership_offer/controllers/membership_detail_controller.dart'; |
| 3 | import 'package:doublefeel_flutter/app/modules/purchase/widgets/purchase_colors.dart'; | 3 | import 'package:doublefeel_flutter/app/modules/purchase/widgets/purchase_colors.dart'; |
| 4 | import 'package:doublefeel_flutter/app/modules/purchase/widgets/refund_explanation_bottom_sheet.dart'; | 4 | import 'package:doublefeel_flutter/app/modules/purchase/widgets/refund_explanation_bottom_sheet.dart'; |
| 5 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 5 | import 'package:doublefeel_flutter/app/widget/premium_benefit_list_view.dart'; | 6 | import 'package:doublefeel_flutter/app/widget/premium_benefit_list_view.dart'; |
| 6 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 7 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 7 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; | 8 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; |
| @@ -249,10 +250,11 @@ class MembershipDetailView extends GetView<MembershipDetailController> { | @@ -249,10 +250,11 @@ class MembershipDetailView extends GetView<MembershipDetailController> { | ||
| 249 | ], | 250 | ], |
| 250 | ), | 251 | ), |
| 251 | ), | 252 | ), |
| 252 | - if (vipInfo?.vipType == 1 || | ||
| 253 | - vipInfo?.vipType == 2 || | ||
| 254 | - vipInfo?.vipType == 3 || | ||
| 255 | - vipInfo?.vipType == 4) | 253 | + if (!isOhos() && |
| 254 | + (vipInfo?.vipType == 1 || | ||
| 255 | + vipInfo?.vipType == 2 || | ||
| 256 | + vipInfo?.vipType == 3 || | ||
| 257 | + vipInfo?.vipType == 4)) | ||
| 256 | GestureDetector( | 258 | GestureDetector( |
| 257 | onTap: () { | 259 | onTap: () { |
| 258 | controller.manageSubscription(); | 260 | controller.manageSubscription(); |
| @@ -365,6 +367,49 @@ class _LegalNotes extends StatelessWidget { | @@ -365,6 +367,49 @@ class _LegalNotes extends StatelessWidget { | ||
| 365 | 367 | ||
| 366 | @override | 368 | @override |
| 367 | Widget build(BuildContext context) { | 369 | Widget build(BuildContext context) { |
| 370 | + if (isOhos()) { | ||
| 371 | + return Padding( | ||
| 372 | + padding: const EdgeInsets.symmetric(horizontal: 16), | ||
| 373 | + child: Column( | ||
| 374 | + crossAxisAlignment: CrossAxisAlignment.start, | ||
| 375 | + children: [ | ||
| 376 | + Text( | ||
| 377 | + context.l10n.purchaseNotesTitle, | ||
| 378 | + style: const TextStyle( | ||
| 379 | + color: Colors.black, | ||
| 380 | + fontSize: 18, | ||
| 381 | + fontWeight: FontWeight.w600, | ||
| 382 | + height: 1.2, | ||
| 383 | + ), | ||
| 384 | + ), | ||
| 385 | + const SizedBox(height: 24), | ||
| 386 | + const _LegalText( | ||
| 387 | + prefix: '1. ', | ||
| 388 | + text: '确认支付成功后,会员权益将立即生效。', | ||
| 389 | + ), | ||
| 390 | + const SizedBox(height: 12), | ||
| 391 | + const _LegalText( | ||
| 392 | + prefix: '2. ', | ||
| 393 | + text: 'DoubleFeel Pro 会员属于虚拟服务商品,购买后除法律法规另有规定外,通常不支持退款。', | ||
| 394 | + ), | ||
| 395 | + const SizedBox(height: 12), | ||
| 396 | + const _LegalText( | ||
| 397 | + prefix: '3. ', | ||
| 398 | + text: '如支付成功后会员未生效,请尝试重新登录账号。', | ||
| 399 | + ), | ||
| 400 | + const SizedBox(height: 12), | ||
| 401 | + _LegalText( | ||
| 402 | + prefix: '4. ', | ||
| 403 | + text: '如果有更多其它问题请', | ||
| 404 | + linkText: context.l10n.purchaseLinkContactUs, | ||
| 405 | + linkSuffix: '。', | ||
| 406 | + onLinkTap: controller.openContactUs, | ||
| 407 | + ), | ||
| 408 | + ], | ||
| 409 | + ), | ||
| 410 | + ); | ||
| 411 | + } | ||
| 412 | + | ||
| 368 | return Padding( | 413 | return Padding( |
| 369 | padding: const EdgeInsets.symmetric(horizontal: 16), | 414 | padding: const EdgeInsets.symmetric(horizontal: 16), |
| 370 | child: Column( | 415 | child: Column( |
| 1 | import 'package:doublefeel_flutter/app/utils/assets_helper.dart'; | 1 | import 'package:doublefeel_flutter/app/utils/assets_helper.dart'; |
| 2 | +import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | ||
| 2 | import 'package:doublefeel_flutter/app/widget/payment_confirmation_bottom_sheet.dart'; | 3 | import 'package:doublefeel_flutter/app/widget/payment_confirmation_bottom_sheet.dart'; |
| 3 | import 'package:doublefeel_flutter/app/widget/premium_benefit_list_view.dart'; | 4 | import 'package:doublefeel_flutter/app/widget/premium_benefit_list_view.dart'; |
| 4 | import 'package:doublefeel_flutter/core/util/size_extensions.dart'; | 5 | import 'package:doublefeel_flutter/core/util/size_extensions.dart'; |
| @@ -265,6 +266,48 @@ class _LegalNotes extends StatelessWidget { | @@ -265,6 +266,48 @@ class _LegalNotes extends StatelessWidget { | ||
| 265 | 266 | ||
| 266 | @override | 267 | @override |
| 267 | Widget build(BuildContext context) { | 268 | Widget build(BuildContext context) { |
| 269 | + if (isOhos()) { | ||
| 270 | + return Padding( | ||
| 271 | + padding: const EdgeInsets.symmetric(horizontal: 16), | ||
| 272 | + child: Column( | ||
| 273 | + crossAxisAlignment: CrossAxisAlignment.start, | ||
| 274 | + children: [ | ||
| 275 | + Text( | ||
| 276 | + context.l10n.purchaseNotesTitle, | ||
| 277 | + style: const TextStyle( | ||
| 278 | + color: Colors.black, | ||
| 279 | + fontSize: 18, | ||
| 280 | + fontWeight: FontWeight.w600, | ||
| 281 | + height: 1.2, | ||
| 282 | + ), | ||
| 283 | + ), | ||
| 284 | + const SizedBox(height: 24), | ||
| 285 | + const _LegalText( | ||
| 286 | + prefix: '1. ', | ||
| 287 | + text: '确认支付成功后,会员权益将立即生效。', | ||
| 288 | + ), | ||
| 289 | + const SizedBox(height: 12), | ||
| 290 | + const _LegalText( | ||
| 291 | + prefix: '2. ', | ||
| 292 | + text: 'DoubleFeel Pro 会员属于虚拟服务商品,购买后除法律法规另有规定外,通常不支持退款。', | ||
| 293 | + ), | ||
| 294 | + const SizedBox(height: 12), | ||
| 295 | + const _LegalText( | ||
| 296 | + prefix: '3. ', | ||
| 297 | + text: '如支付成功后会员未生效,请尝试重新登录账号。', | ||
| 298 | + ), | ||
| 299 | + const SizedBox(height: 12), | ||
| 300 | + _LegalText( | ||
| 301 | + prefix: '4. ', | ||
| 302 | + text: '如果有更多其它问题请', | ||
| 303 | + linkText: context.l10n.purchaseLinkContactUs, | ||
| 304 | + linkSuffix: '。', | ||
| 305 | + onLinkTap: controller.openContactUs, | ||
| 306 | + ), | ||
| 307 | + ], | ||
| 308 | + ), | ||
| 309 | + ); | ||
| 310 | + } | ||
| 268 | return Padding( | 311 | return Padding( |
| 269 | padding: const EdgeInsets.symmetric(horizontal: 16), | 312 | padding: const EdgeInsets.symmetric(horizontal: 16), |
| 270 | child: Column( | 313 | child: Column( |
| @@ -2,10 +2,8 @@ import 'dart:io'; | @@ -2,10 +2,8 @@ import 'dart:io'; | ||
| 2 | 2 | ||
| 3 | bool isOhos() { | 3 | bool isOhos() { |
| 4 | return Platform.isOhos; | 4 | return Platform.isOhos; |
| 5 | - // return true; | ||
| 6 | } | 5 | } |
| 7 | 6 | ||
| 8 | bool isIOS() { | 7 | bool isIOS() { |
| 9 | return Platform.isIOS; | 8 | return Platform.isIOS; |
| 10 | - // return false; | ||
| 11 | } | 9 | } |
| @@ -3,6 +3,8 @@ | @@ -3,6 +3,8 @@ | ||
| 3 | "analyzerMode":"BEFORE_RELEASE_CHECK_MODE", | 3 | "analyzerMode":"BEFORE_RELEASE_CHECK_MODE", |
| 4 | "autoCompile":true, | 4 | "autoCompile":true, |
| 5 | "autoTest":true, | 5 | "autoTest":true, |
| 6 | + "cardCode":"auto_before_release_check", | ||
| 7 | + "cardEnName":"Auto Release Experience Compliance Check", | ||
| 6 | "cardId":200, | 8 | "cardId":200, |
| 7 | "cardName":"自动上架体验规范体检", | 9 | "cardName":"自动上架体验规范体检", |
| 8 | "coldStartCount":0, | 10 | "coldStartCount":0, |
| @@ -3,6 +3,8 @@ | @@ -3,6 +3,8 @@ | ||
| 3 | "analyzerMode":"BEFORE_RELEASE_CHECK_MODE", | 3 | "analyzerMode":"BEFORE_RELEASE_CHECK_MODE", |
| 4 | "autoCompile":true, | 4 | "autoCompile":true, |
| 5 | "autoTest":true, | 5 | "autoTest":true, |
| 6 | + "cardCode":"auto_before_release_check", | ||
| 7 | + "cardEnName":"Auto Release Experience Compliance Check", | ||
| 6 | "cardId":300, | 8 | "cardId":300, |
| 7 | "cardName":"自动上架体验规范体检", | 9 | "cardName":"自动上架体验规范体检", |
| 8 | "coldStartCount":0, | 10 | "coldStartCount":0, |
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | "vendor": "DiDi", | 4 | "vendor": "DiDi", |
| 5 | "versionCode": 100, | 5 | "versionCode": 100, |
| 6 | "versionName": "2.7.0", | 6 | "versionName": "2.7.0", |
| 7 | - "icon": "$media:app_icon", | 7 | + "icon": "$media:layered_image", |
| 8 | "label": "$string:app_name" | 8 | "label": "$string:app_name" |
| 9 | } | 9 | } |
| 10 | } | 10 | } |
3.86 KB
6.29 KB
34.6 KB
| @@ -93,6 +93,9 @@ export default class EntryAbility extends FlutterAbility implements MethodCallHa | @@ -93,6 +93,9 @@ export default class EntryAbility extends FlutterAbility implements MethodCallHa | ||
| 93 | onForeground(): void { | 93 | onForeground(): void { |
| 94 | super.onForeground() | 94 | super.onForeground() |
| 95 | NativePigeonRegistrar.setAppForeground(true) | 95 | NativePigeonRegistrar.setAppForeground(true) |
| 96 | + // A normal return from WeChat has no SendAuthResp callback. Release the | ||
| 97 | + // pending authorization request so Flutter can remove its loading overlay. | ||
| 98 | + this.weChatLoginBridge?.cancelPendingAuthorization() | ||
| 96 | } | 99 | } |
| 97 | 100 | ||
| 98 | onBackground(): void { | 101 | onBackground(): void { |
| @@ -34,24 +34,24 @@ export class WeChatLoginBridge extends WeChatHostApi implements WXApiEventHandle | @@ -34,24 +34,24 @@ export class WeChatLoginBridge extends WeChatHostApi implements WXApiEventHandle | ||
| 34 | if (WECHAT_APP_ID.length === 0) { | 34 | if (WECHAT_APP_ID.length === 0) { |
| 35 | result.error(new FlutterError( | 35 | result.error(new FlutterError( |
| 36 | 'wechat_app_id_missing', | 36 | 'wechat_app_id_missing', |
| 37 | - 'WeChatError', | ||
| 38 | 'Set WECHAT_APP_ID in WeChatLoginBridge.ets first.', | 37 | 'Set WECHAT_APP_ID in WeChatLoginBridge.ets first.', |
| 38 | + 'WeChatError', | ||
| 39 | )); | 39 | )); |
| 40 | return; | 40 | return; |
| 41 | } | 41 | } |
| 42 | if (this.pendingResult !== null) { | 42 | if (this.pendingResult !== null) { |
| 43 | result.error(new FlutterError( | 43 | result.error(new FlutterError( |
| 44 | 'wechat_authorization_in_progress', | 44 | 'wechat_authorization_in_progress', |
| 45 | - 'WeChatError', | ||
| 46 | 'WeChat authorization is already in progress.', | 45 | 'WeChat authorization is already in progress.', |
| 46 | + 'WeChatError', | ||
| 47 | )); | 47 | )); |
| 48 | return; | 48 | return; |
| 49 | } | 49 | } |
| 50 | if (!this.wxApi.isWXAppInstalled()) { | 50 | if (!this.wxApi.isWXAppInstalled()) { |
| 51 | result.error(new FlutterError( | 51 | result.error(new FlutterError( |
| 52 | 'wechat_not_installed', | 52 | 'wechat_not_installed', |
| 53 | - 'WeChatError', | ||
| 54 | 'WeChat is not installed.', | 53 | 'WeChat is not installed.', |
| 54 | + 'WeChatError', | ||
| 55 | )); | 55 | )); |
| 56 | return; | 56 | return; |
| 57 | } | 57 | } |
| @@ -80,6 +80,21 @@ export class WeChatLoginBridge extends WeChatHostApi implements WXApiEventHandle | @@ -80,6 +80,21 @@ export class WeChatLoginBridge extends WeChatHostApi implements WXApiEventHandle | ||
| 80 | this.wxApi.handleWant(want, this); | 80 | this.wxApi.handleWant(want, this); |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | + /** | ||
| 84 | + * WeChat does not send an authorization response when the user simply | ||
| 85 | + * returns to our app. Complete the pending Pigeon call in that case, so the | ||
| 86 | + * Flutter loading overlay is always dismissed. | ||
| 87 | + */ | ||
| 88 | + cancelPendingAuthorization(): void { | ||
| 89 | + if (this.pendingResult === null) { | ||
| 90 | + return; | ||
| 91 | + } | ||
| 92 | + this.finishWithError( | ||
| 93 | + 'wechat_authorization_cancelled', | ||
| 94 | + 'WeChat authorization was cancelled.', | ||
| 95 | + ); | ||
| 96 | + } | ||
| 97 | + | ||
| 83 | onReq(_request: BaseReq): void {} | 98 | onReq(_request: BaseReq): void {} |
| 84 | 99 | ||
| 85 | onResp(response: BaseResp): void { | 100 | onResp(response: BaseResp): void { |
| @@ -114,7 +129,7 @@ export class WeChatLoginBridge extends WeChatHostApi implements WXApiEventHandle | @@ -114,7 +129,7 @@ export class WeChatLoginBridge extends WeChatHostApi implements WXApiEventHandle | ||
| 114 | const result = this.pendingResult; | 129 | const result = this.pendingResult; |
| 115 | this.pendingResult = null; | 130 | this.pendingResult = null; |
| 116 | if (result !== null) { | 131 | if (result !== null) { |
| 117 | - result.error(new FlutterError(code, 'WeChatError', message)); | 132 | + result.error(new FlutterError(code, message, 'WeChatError')); |
| 118 | } | 133 | } |
| 119 | } | 134 | } |
| 120 | } | 135 | } |
| @@ -22,9 +22,9 @@ | @@ -22,9 +22,9 @@ | ||
| 22 | "name": "EntryAbility", | 22 | "name": "EntryAbility", |
| 23 | "srcEntry": "./ets/entryability/EntryAbility.ets", | 23 | "srcEntry": "./ets/entryability/EntryAbility.ets", |
| 24 | "description": "$string:EntryAbility_desc", | 24 | "description": "$string:EntryAbility_desc", |
| 25 | - "icon": "$media:icon", | 25 | + "icon": "$media:layered_image", |
| 26 | "label": "$string:EntryAbility_label", | 26 | "label": "$string:EntryAbility_label", |
| 27 | - "startWindowIcon": "$media:icon", | 27 | + "startWindowIcon": "$media:startIcon", |
| 28 | "startWindowBackground": "$color:start_window_background", | 28 | "startWindowBackground": "$color:start_window_background", |
| 29 | "exported": true, | 29 | "exported": true, |
| 30 | "skills": [ | 30 | "skills": [ |
6.29 KB
34.6 KB
3.86 KB
56.8 KB
| @@ -16,10 +16,10 @@ | @@ -16,10 +16,10 @@ | ||
| 16 | "name": "TestAbility", | 16 | "name": "TestAbility", |
| 17 | "srcEntry": "./ets/testability/TestAbility.ets", | 17 | "srcEntry": "./ets/testability/TestAbility.ets", |
| 18 | "description": "$string:TestAbility_desc", | 18 | "description": "$string:TestAbility_desc", |
| 19 | - "icon": "$media:icon", | 19 | + "icon": "$media:layered_image", |
| 20 | "label": "$string:TestAbility_label", | 20 | "label": "$string:TestAbility_label", |
| 21 | "exported": true, | 21 | "exported": true, |
| 22 | - "startWindowIcon": "$media:icon", | 22 | + "startWindowIcon": "$media:startIcon", |
| 23 | "startWindowBackground": "$color:start_window_background", | 23 | "startWindowBackground": "$color:start_window_background", |
| 24 | "skills": [ | 24 | "skills": [ |
| 25 | { | 25 | { |
6.29 KB
34.6 KB
6.63 KB
56.8 KB
-
Please register or login to post a comment