Showing
12 changed files
with
84 additions
and
189 deletions
| 1 | -import 'package:doublefeel_flutter/core/network/api/click_event_api.dart'; | ||
| 2 | import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; | 1 | import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; |
| 3 | import 'package:get/get.dart'; | 2 | import 'package:get/get.dart'; |
| 4 | 3 | ||
| @@ -56,7 +55,6 @@ void registerUserSessionDeps() { | @@ -56,7 +55,6 @@ void registerUserSessionDeps() { | ||
| 56 | Get.put(UserApi(dioClient), permanent: true); | 55 | Get.put(UserApi(dioClient), permanent: true); |
| 57 | Get.put(VipApi(dioClient), permanent: true); | 56 | Get.put(VipApi(dioClient), permanent: true); |
| 58 | Get.put(FriendApi(dioClient), permanent: true); | 57 | Get.put(FriendApi(dioClient), permanent: true); |
| 59 | - Get.put(ClickEventApi(dioClient), permanent: true); | ||
| 60 | Get.put<ImService>(ImServiceStub(), permanent: true); | 58 | Get.put<ImService>(ImServiceStub(), permanent: true); |
| 61 | Get.put( | 59 | Get.put( |
| 62 | ThinkingDataService(Get.find<AppEnvironmentConfig>()), | 60 | ThinkingDataService(Get.find<AppEnvironmentConfig>()), |
| @@ -38,7 +38,7 @@ class AppReviewPromptLogic { | @@ -38,7 +38,7 @@ class AppReviewPromptLogic { | ||
| 38 | final result = await showDialog<AppReviewPromptResult>( | 38 | final result = await showDialog<AppReviewPromptResult>( |
| 39 | context: context, | 39 | context: context, |
| 40 | barrierColor: Colors.black.withValues(alpha: 0.7), | 40 | barrierColor: Colors.black.withValues(alpha: 0.7), |
| 41 | - barrierDismissible: false, | 41 | + barrierDismissible: true, |
| 42 | builder: (_) => const AppReviewPromptDialog(), | 42 | builder: (_) => const AppReviewPromptDialog(), |
| 43 | ); | 43 | ); |
| 44 | 44 |
| 1 | -import 'package:doublefeel_flutter/core/constants/event_const.dart'; | ||
| 2 | import 'package:doublefeel_flutter/data/models/friend/friend_models.dart'; | 1 | import 'package:doublefeel_flutter/data/models/friend/friend_models.dart'; |
| 3 | import 'package:get/get.dart'; | 2 | import 'package:get/get.dart'; |
| 4 | 3 | ||
| 5 | -import '../../../../core/network/api/click_event_api.dart'; | ||
| 6 | import '../../health_trend/controllers/health_trend_analytics.dart'; | 4 | import '../../health_trend/controllers/health_trend_analytics.dart'; |
| 7 | import '../../health_trend/controllers/health_trend_control.dart'; | 5 | import '../../health_trend/controllers/health_trend_control.dart'; |
| 8 | import '../../report_common/models/health_report_query.dart'; | 6 | import '../../report_common/models/health_report_query.dart'; |
| @@ -70,12 +68,5 @@ class FriendTrendController extends GetxController with HealthTrendControl { | @@ -70,12 +68,5 @@ class FriendTrendController extends GetxController with HealthTrendControl { | ||
| 70 | selectedTypeIndex.value, | 68 | selectedTypeIndex.value, |
| 71 | userRole: '好友的', | 69 | userRole: '好友的', |
| 72 | ); | 70 | ); |
| 73 | - _trackViewFriendSleepStatsIfNeeded(); | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - void _trackViewFriendSleepStatsIfNeeded() { | ||
| 77 | - if (selectedTypeIndex.value != 2) return; | ||
| 78 | - if (!Get.isRegistered<ClickEventApi>()) return; | ||
| 79 | - Get.find<ClickEventApi>().postClickEvent(EventConst.viewFriendSleepStats); | ||
| 80 | } | 71 | } |
| 81 | } | 72 | } |
| @@ -3,10 +3,12 @@ import 'package:get/get.dart'; | @@ -3,10 +3,12 @@ import 'package:get/get.dart'; | ||
| 3 | import 'package:lottie/lottie.dart'; | 3 | import 'package:lottie/lottie.dart'; |
| 4 | 4 | ||
| 5 | import '../../../../core/constants/intent_keys.dart'; | 5 | import '../../../../core/constants/intent_keys.dart'; |
| 6 | +import '../../../../data/local/local_storage.dart'; | ||
| 6 | import '../../../../data/local/user_preferences_storage.dart'; | 7 | import '../../../../data/local/user_preferences_storage.dart'; |
| 7 | import '../../../routes/app_pages.dart'; | 8 | import '../../../routes/app_pages.dart'; |
| 8 | import '../../activity_burn_report/controllers/activity_burn_report_logic.dart'; | 9 | import '../../activity_burn_report/controllers/activity_burn_report_logic.dart'; |
| 9 | import '../../activity_burn_report/views/activity_burn_report_view.dart'; | 10 | import '../../activity_burn_report/views/activity_burn_report_view.dart'; |
| 11 | +import '../../app_review_prompt/logic/app_review_prompt_logic.dart'; | ||
| 10 | import '../../home/widgets/trend/trend_type_tab_bar.dart'; | 12 | import '../../home/widgets/trend/trend_type_tab_bar.dart'; |
| 11 | import '../../hrv_report/controllers/hrv_report_logic.dart'; | 13 | import '../../hrv_report/controllers/hrv_report_logic.dart'; |
| 12 | import '../../hrv_report/views/hrv_report_view.dart'; | 14 | import '../../hrv_report/views/hrv_report_view.dart'; |
| @@ -417,6 +419,7 @@ class _SleepTrendSection extends StatefulWidget { | @@ -417,6 +419,7 @@ class _SleepTrendSection extends StatefulWidget { | ||
| 417 | 419 | ||
| 418 | class _SleepTrendSectionState extends State<_SleepTrendSection> { | 420 | class _SleepTrendSectionState extends State<_SleepTrendSection> { |
| 419 | late final SleepReportLogic _logic; | 421 | late final SleepReportLogic _logic; |
| 422 | + late final AppReviewPromptLogic _appReviewPromptLogic; | ||
| 420 | late final Worker _periodWorker; | 423 | late final Worker _periodWorker; |
| 421 | late final Worker _dateWorker; | 424 | late final Worker _dateWorker; |
| 422 | var _syncingExternalQuery = false; | 425 | var _syncingExternalQuery = false; |
| @@ -428,6 +431,9 @@ class _SleepTrendSectionState extends State<_SleepTrendSection> { | @@ -428,6 +431,9 @@ class _SleepTrendSectionState extends State<_SleepTrendSection> { | ||
| 428 | _logic = SleepReportLogic( | 431 | _logic = SleepReportLogic( |
| 429 | initialTargetUserId: widget.query.targetUserId, | 432 | initialTargetUserId: widget.query.targetUserId, |
| 430 | ); | 433 | ); |
| 434 | + _appReviewPromptLogic = AppReviewPromptLogic( | ||
| 435 | + storage: Get.find<LocalStorage>(), | ||
| 436 | + ); | ||
| 431 | _logic.initializeQuery(widget.query.period, widget.query.date); | 437 | _logic.initializeQuery(widget.query.period, widget.query.date); |
| 432 | _periodWorker = ever(_logic.selectedPeriod, (period) { | 438 | _periodWorker = ever(_logic.selectedPeriod, (period) { |
| 433 | _scheduleQueryNotification(); | 439 | _scheduleQueryNotification(); |
| @@ -489,6 +495,9 @@ class _SleepTrendSectionState extends State<_SleepTrendSection> { | @@ -489,6 +495,9 @@ class _SleepTrendSectionState extends State<_SleepTrendSection> { | ||
| 489 | logic: _logic, | 495 | logic: _logic, |
| 490 | isVip: widget.isVip, | 496 | isVip: widget.isVip, |
| 491 | onSubscribe: widget.onSubscribe, | 497 | onSubscribe: widget.onSubscribe, |
| 498 | + onReportViewed: widget.isSelected | ||
| 499 | + ? () => _appReviewPromptLogic.maybeShowPrompt(context) | ||
| 500 | + : null, | ||
| 492 | loadingIndicator: const _HealthTrendLoadingIndicator(), | 501 | loadingIndicator: const _HealthTrendLoadingIndicator(), |
| 493 | ); | 502 | ); |
| 494 | } | 503 | } |
| 1 | import 'package:doublefeel_flutter/app/modules/friends/controllers/friend_trend_controller.dart'; | 1 | import 'package:doublefeel_flutter/app/modules/friends/controllers/friend_trend_controller.dart'; |
| 2 | import 'package:doublefeel_flutter/app/modules/friends/controllers/friends_controller.dart'; | 2 | import 'package:doublefeel_flutter/app/modules/friends/controllers/friends_controller.dart'; |
| 3 | -import 'package:doublefeel_flutter/core/constants/event_const.dart'; | ||
| 4 | import 'package:doublefeel_flutter/core/constants/intent_keys.dart'; | 3 | import 'package:doublefeel_flutter/core/constants/intent_keys.dart'; |
| 5 | -import 'package:doublefeel_flutter/core/network/api/click_event_api.dart'; | ||
| 6 | import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; | 4 | import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; |
| 7 | import 'package:doublefeel_flutter/core/result/app_result.dart'; | 5 | import 'package:doublefeel_flutter/core/result/app_result.dart'; |
| 8 | import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; | 6 | import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; |
| @@ -15,7 +13,6 @@ import 'package:flutter/services.dart'; | @@ -15,7 +13,6 @@ import 'package:flutter/services.dart'; | ||
| 15 | import 'package:get/get.dart'; | 13 | import 'package:get/get.dart'; |
| 16 | 14 | ||
| 17 | import '../../../routes/app_pages.dart'; | 15 | import '../../../routes/app_pages.dart'; |
| 18 | -import '../../app_review_prompt/logic/app_review_prompt_logic.dart'; | ||
| 19 | import '../../purchase/purchase_route_args.dart'; | 16 | import '../../purchase/purchase_route_args.dart'; |
| 20 | import '../../report_common/models/report_period.dart'; | 17 | import '../../report_common/models/report_period.dart'; |
| 21 | import 'today_controller.dart'; | 18 | import 'today_controller.dart'; |
| @@ -31,9 +28,6 @@ class HomeController extends GetxController { | @@ -31,9 +28,6 @@ class HomeController extends GetxController { | ||
| 31 | static const friendsTabIndex = 2; | 28 | static const friendsTabIndex = 2; |
| 32 | static const _mainChannel = MethodChannel('doublefeel_flutter_main_channel'); | 29 | static const _mainChannel = MethodChannel('doublefeel_flutter_main_channel'); |
| 33 | 30 | ||
| 34 | - final AppReviewPromptLogic _appReviewPromptLogic = AppReviewPromptLogic( | ||
| 35 | - storage: Get.find<LocalStorage>(), | ||
| 36 | - ); | ||
| 37 | final LocalStorage _storage = Get.find<LocalStorage>(); | 31 | final LocalStorage _storage = Get.find<LocalStorage>(); |
| 38 | final UserStateService userStateService = Get.find<UserStateService>(); | 32 | final UserStateService userStateService = Get.find<UserStateService>(); |
| 39 | final PlatformHostApi _platformHostApi = PlatformHostApi(); | 33 | final PlatformHostApi _platformHostApi = PlatformHostApi(); |
| @@ -90,30 +84,6 @@ class HomeController extends GetxController { | @@ -90,30 +84,6 @@ class HomeController extends GetxController { | ||
| 90 | return url.isNotEmpty; | 84 | return url.isNotEmpty; |
| 91 | } | 85 | } |
| 92 | 86 | ||
| 93 | - Future<void> maybeShowAppReviewPrompt(BuildContext context) async { | ||
| 94 | - if (!_appReviewPromptLogic.canShowPrompt(context)) return; | ||
| 95 | - | ||
| 96 | - final canShowPrompt = await _hasReviewPromptClickEvent(); | ||
| 97 | - if (!canShowPrompt) return; | ||
| 98 | - | ||
| 99 | - await _appReviewPromptLogic.maybeShowPrompt(context); | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - Future<bool> _hasReviewPromptClickEvent() async { | ||
| 103 | - if (!Get.isRegistered<ClickEventApi>()) return false; | ||
| 104 | - | ||
| 105 | - final result = await Get.find<ClickEventApi>().getClickEvent(); | ||
| 106 | - return switch (result) { | ||
| 107 | - AppSuccess(:final data) => data.list?.any( | ||
| 108 | - (item) => | ||
| 109 | - EventConst.keys.contains(item.eventName) && | ||
| 110 | - (item.clickCnt ?? 0) > 0, | ||
| 111 | - ) ?? | ||
| 112 | - false, | ||
| 113 | - AppFailure() => false, | ||
| 114 | - }; | ||
| 115 | - } | ||
| 116 | - | ||
| 117 | Future<bool> maybeShowMembershipOffer() async { | 87 | Future<bool> maybeShowMembershipOffer() async { |
| 118 | if (isFromOnboard) { | 88 | if (isFromOnboard) { |
| 119 | isFromOnboard = false; | 89 | isFromOnboard = false; |
| @@ -193,7 +163,6 @@ class HomeController extends GetxController { | @@ -193,7 +163,6 @@ class HomeController extends GetxController { | ||
| 193 | final params = uri.queryParameters; // {"tab": "today"} 等 | 163 | final params = uri.queryParameters; // {"tab": "today"} 等 |
| 194 | 164 | ||
| 195 | _dispatchRoute(path, params); | 165 | _dispatchRoute(path, params); |
| 196 | - _executePostClickEvent(path, params); | ||
| 197 | } catch (_) {} | 166 | } catch (_) {} |
| 198 | } | 167 | } |
| 199 | 168 | ||
| @@ -244,24 +213,6 @@ class HomeController extends GetxController { | @@ -244,24 +213,6 @@ class HomeController extends GetxController { | ||
| 244 | } | 213 | } |
| 245 | } | 214 | } |
| 246 | 215 | ||
| 247 | - void _executePostClickEvent(String path, Map<String, String> params) { | ||
| 248 | - try { | ||
| 249 | - var isHrvChange = params['is_hrv_change'] == "1"; | ||
| 250 | - if (isHrvChange) { | ||
| 251 | - final clickEventApi = Get.find<ClickEventApi>(); | ||
| 252 | - if (!Get.isRegistered<ClickEventApi>()) return; | ||
| 253 | - switch (path) { | ||
| 254 | - case AppRoutes.home: | ||
| 255 | - clickEventApi.postClickEvent(EventConst.hrvChangeClick); | ||
| 256 | - break; | ||
| 257 | - case Routes.FRIEND_HOME: | ||
| 258 | - clickEventApi.postClickEvent(EventConst.friendHrvChangeClick); | ||
| 259 | - break; | ||
| 260 | - } | ||
| 261 | - } | ||
| 262 | - } catch (e) {} | ||
| 263 | - } | ||
| 264 | - | ||
| 265 | /// 根据 tab 名称切换首页底部 tab | 216 | /// 根据 tab 名称切换首页底部 tab |
| 266 | void _switchHomeTab(String? tab, Map<String, String> params) { | 217 | void _switchHomeTab(String? tab, Map<String, String> params) { |
| 267 | switch (tab) { | 218 | switch (tab) { |
| 1 | -import 'package:doublefeel_flutter/core/constants/event_const.dart'; | ||
| 2 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
| 3 | import 'package:get/get.dart'; | 2 | import 'package:get/get.dart'; |
| 4 | 3 | ||
| 5 | -import '../../../../../core/network/api/click_event_api.dart'; | ||
| 6 | import '../../../../../core/network/api/friend_api.dart'; | 4 | import '../../../../../core/network/api/friend_api.dart'; |
| 7 | import '../../../../../core/result/app_result.dart'; | 5 | import '../../../../../core/result/app_result.dart'; |
| 8 | import '../../../../../data/models/friend/friend_models.dart'; | 6 | import '../../../../../data/models/friend/friend_models.dart'; |
| @@ -80,7 +78,6 @@ class TrendController extends GetxController with HealthTrendControl { | @@ -80,7 +78,6 @@ class TrendController extends GetxController with HealthTrendControl { | ||
| 80 | if (userId == targetUserId.value) return; | 78 | if (userId == targetUserId.value) return; |
| 81 | targetUserId.value = userId; | 79 | targetUserId.value = userId; |
| 82 | refreshToken.value++; | 80 | refreshToken.value++; |
| 83 | - _trackViewSleepStatsIfNeeded(); | ||
| 84 | } | 81 | } |
| 85 | 82 | ||
| 86 | void selectFriend(FriendItem friendInfo) { | 83 | void selectFriend(FriendItem friendInfo) { |
| @@ -171,19 +168,5 @@ class TrendController extends GetxController with HealthTrendControl { | @@ -171,19 +168,5 @@ class TrendController extends GetxController with HealthTrendControl { | ||
| 171 | selectedTypeIndex.value, | 168 | selectedTypeIndex.value, |
| 172 | userRole: targetFriendInfo.value == null ? '我的' : '好友的', | 169 | userRole: targetFriendInfo.value == null ? '我的' : '好友的', |
| 173 | ); | 170 | ); |
| 174 | - _trackViewSleepStatsIfNeeded(); | ||
| 175 | - } | ||
| 176 | - | ||
| 177 | - void _trackViewSleepStatsIfNeeded() { | ||
| 178 | - if (!_isPageVisible || | ||
| 179 | - selectedTypeIndex.value != TrendType.sleep.tabIndex) { | ||
| 180 | - return; | ||
| 181 | - } | ||
| 182 | - | ||
| 183 | - final eventName = targetUserId.value == null | ||
| 184 | - ? EventConst.viewSleepStats | ||
| 185 | - : EventConst.viewFriendSleepStats; | ||
| 186 | - if (!Get.isRegistered<ClickEventApi>()) return; | ||
| 187 | - Get.find<ClickEventApi>().postClickEvent(eventName); | ||
| 188 | } | 171 | } |
| 189 | } | 172 | } |
| @@ -65,8 +65,6 @@ class _HomeReviewPromptGateState extends State<_HomeReviewPromptGate> { | @@ -65,8 +65,6 @@ class _HomeReviewPromptGateState extends State<_HomeReviewPromptGate> { | ||
| 65 | WidgetsBinding.instance.addPostFrameCallback((_) async { | 65 | WidgetsBinding.instance.addPostFrameCallback((_) async { |
| 66 | if (!mounted) return; | 66 | if (!mounted) return; |
| 67 | await widget.controller.maybeShowMembershipOffer(); | 67 | await widget.controller.maybeShowMembershipOffer(); |
| 68 | - if (!mounted) return; | ||
| 69 | - widget.controller.maybeShowAppReviewPrompt(context); | ||
| 70 | }); | 68 | }); |
| 71 | } | 69 | } |
| 72 | 70 |
| @@ -16,21 +16,77 @@ import '../widgets/sleep_quality_ring.dart'; | @@ -16,21 +16,77 @@ import '../widgets/sleep_quality_ring.dart'; | ||
| 16 | import '../widgets/sleep_summary_card.dart'; | 16 | import '../widgets/sleep_summary_card.dart'; |
| 17 | import '../widgets/sleep_week_report_view.dart'; | 17 | import '../widgets/sleep_week_report_view.dart'; |
| 18 | 18 | ||
| 19 | -class SleepReportView extends StatelessWidget { | 19 | +class SleepReportView extends StatefulWidget { |
| 20 | const SleepReportView({ | 20 | const SleepReportView({ |
| 21 | super.key, | 21 | super.key, |
| 22 | required this.logic, | 22 | required this.logic, |
| 23 | required this.isVip, | 23 | required this.isVip, |
| 24 | required this.onSubscribe, | 24 | required this.onSubscribe, |
| 25 | + this.onReportViewed, | ||
| 25 | this.loadingIndicator = const ReportLoadingIndicator(), | 26 | this.loadingIndicator = const ReportLoadingIndicator(), |
| 26 | }); | 27 | }); |
| 27 | 28 | ||
| 28 | final SleepReportLogic logic; | 29 | final SleepReportLogic logic; |
| 29 | final bool isVip; | 30 | final bool isVip; |
| 30 | final ValueChanged<String> onSubscribe; | 31 | final ValueChanged<String> onSubscribe; |
| 32 | + final Future<void> Function()? onReportViewed; | ||
| 31 | final Widget loadingIndicator; | 33 | final Widget loadingIndicator; |
| 32 | 34 | ||
| 33 | @override | 35 | @override |
| 36 | + State<SleepReportView> createState() => _SleepReportViewState(); | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +class _SleepReportViewState extends State<SleepReportView> { | ||
| 40 | + String? _lastViewedReportKey; | ||
| 41 | + bool _viewNotificationScheduled = false; | ||
| 42 | + | ||
| 43 | + @override | ||
| 44 | + void didUpdateWidget(covariant SleepReportView oldWidget) { | ||
| 45 | + super.didUpdateWidget(oldWidget); | ||
| 46 | + if (oldWidget.onReportViewed == null && widget.onReportViewed != null) { | ||
| 47 | + _scheduleViewedReportNotification(); | ||
| 48 | + } | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + void _scheduleViewedReportNotification() { | ||
| 52 | + if (_viewNotificationScheduled || widget.onReportViewed == null) return; | ||
| 53 | + | ||
| 54 | + final reportKey = _loadedReportKey(); | ||
| 55 | + if (reportKey == null || reportKey == _lastViewedReportKey) return; | ||
| 56 | + | ||
| 57 | + _viewNotificationScheduled = true; | ||
| 58 | + WidgetsBinding.instance.addPostFrameCallback((_) async { | ||
| 59 | + _viewNotificationScheduled = false; | ||
| 60 | + if (!mounted || widget.onReportViewed == null) return; | ||
| 61 | + if (_loadedReportKey() != reportKey || | ||
| 62 | + _lastViewedReportKey == reportKey) { | ||
| 63 | + return; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + _lastViewedReportKey = reportKey; | ||
| 67 | + await widget.onReportViewed!(); | ||
| 68 | + }); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + String? _loadedReportKey() { | ||
| 72 | + if (widget.logic.isLoading.value) return null; | ||
| 73 | + | ||
| 74 | + final period = widget.logic.selectedPeriod.value; | ||
| 75 | + final hasData = switch (period) { | ||
| 76 | + ReportPeriod.day => widget.logic.report.value?.hasData ?? false, | ||
| 77 | + ReportPeriod.week => | ||
| 78 | + widget.logic.weeklyReport.value?.daysWithData.isNotEmpty ?? false, | ||
| 79 | + ReportPeriod.month => | ||
| 80 | + widget.logic.monthlyReport.value?.daysWithData.isNotEmpty ?? false, | ||
| 81 | + ReportPeriod.year => false, | ||
| 82 | + }; | ||
| 83 | + if (!hasData) return null; | ||
| 84 | + | ||
| 85 | + return '${widget.logic.targetUserId.value ?? 'self'}:' | ||
| 86 | + '${period.name}:${widget.logic.selectedDate.value.toIso8601String()}'; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + @override | ||
| 34 | Widget build(BuildContext context) { | 90 | Widget build(BuildContext context) { |
| 35 | return Container( | 91 | return Container( |
| 36 | color: Colors.transparent, | 92 | color: Colors.transparent, |
| @@ -45,25 +101,27 @@ class SleepReportView extends StatelessWidget { | @@ -45,25 +101,27 @@ class SleepReportView extends StatelessWidget { | ||
| 45 | const SizedBox(height: 8), | 101 | const SizedBox(height: 8), |
| 46 | Obx( | 102 | Obx( |
| 47 | () => ReportPeriodTabBar( | 103 | () => ReportPeriodTabBar( |
| 48 | - selectedPeriod: logic.selectedPeriod.value, | ||
| 49 | - onChanged: logic.selectPeriod, | ||
| 50 | - lockedPeriods: isVip | 104 | + selectedPeriod: widget.logic.selectedPeriod.value, |
| 105 | + onChanged: widget.logic.selectPeriod, | ||
| 106 | + lockedPeriods: widget.isVip | ||
| 51 | ? const {} | 107 | ? const {} |
| 52 | : const {ReportPeriod.week, ReportPeriod.month}, | 108 | : const {ReportPeriod.week, ReportPeriod.month}, |
| 53 | - onLockedPeriodTap: (_) => onSubscribe('非会员切换月周-睡眠报告'), | 109 | + onLockedPeriodTap: (_) => |
| 110 | + widget.onSubscribe('非会员切换月周-睡眠报告'), | ||
| 54 | ), | 111 | ), |
| 55 | ), | 112 | ), |
| 56 | const SizedBox(height: 8), | 113 | const SizedBox(height: 8), |
| 57 | - _DateSwitcher(logic: logic), | 114 | + _DateSwitcher(logic: widget.logic), |
| 58 | ], | 115 | ], |
| 59 | ), | 116 | ), |
| 60 | ), | 117 | ), |
| 61 | Expanded( | 118 | Expanded( |
| 62 | child: Obx( | 119 | child: Obx( |
| 63 | () { | 120 | () { |
| 64 | - if (logic.isLoading.value) { | ||
| 65 | - return loadingIndicator; | 121 | + if (widget.logic.isLoading.value) { |
| 122 | + return widget.loadingIndicator; | ||
| 66 | } | 123 | } |
| 124 | + _scheduleViewedReportNotification(); | ||
| 67 | return CustomScrollView( | 125 | return CustomScrollView( |
| 68 | physics: const ClampingScrollPhysics(), | 126 | physics: const ClampingScrollPhysics(), |
| 69 | slivers: [ | 127 | slivers: [ |
| @@ -71,19 +129,21 @@ class SleepReportView extends StatelessWidget { | @@ -71,19 +129,21 @@ class SleepReportView extends StatelessWidget { | ||
| 71 | padding: const EdgeInsets.fromLTRB(16, 20, 16, 0), | 129 | padding: const EdgeInsets.fromLTRB(16, 20, 16, 0), |
| 72 | sliver: SliverList( | 130 | sliver: SliverList( |
| 73 | delegate: SliverChildListDelegate([ | 131 | delegate: SliverChildListDelegate([ |
| 74 | - if (logic.selectedPeriod.value == | 132 | + if (widget.logic.selectedPeriod.value == |
| 75 | ReportPeriod.week) | 133 | ReportPeriod.week) |
| 76 | SleepWeekReportView( | 134 | SleepWeekReportView( |
| 77 | - report: logic.weeklyReport.value, | 135 | + report: widget.logic.weeklyReport.value, |
| 78 | ) | 136 | ) |
| 79 | - else if (logic.selectedPeriod.value == | 137 | + else if (widget.logic.selectedPeriod.value == |
| 80 | ReportPeriod.month) | 138 | ReportPeriod.month) |
| 81 | SleepMonthReportView( | 139 | SleepMonthReportView( |
| 82 | - monthStart: logic.monthStart, | ||
| 83 | - report: logic.monthlyReport.value, | 140 | + monthStart: widget.logic.monthStart, |
| 141 | + report: widget.logic.monthlyReport.value, | ||
| 84 | ) | 142 | ) |
| 85 | else | 143 | else |
| 86 | - _DailyReportContent(report: logic.report.value), | 144 | + _DailyReportContent( |
| 145 | + report: widget.logic.report.value, | ||
| 146 | + ), | ||
| 87 | const ReportBottomSlogan(), | 147 | const ReportBottomSlogan(), |
| 88 | ]), | 148 | ]), |
| 89 | ), | 149 | ), |
lib/core/constants/event_const.dart
deleted
100644 → 0
| 1 | -abstract final class EventConst { | ||
| 2 | - static const String hrvChangeClick = "hrv_change_click"; | ||
| 3 | - static const String friendHrvChangeClick = "friend_hrv_change_click"; | ||
| 4 | - static const String viewSleepStats = "view_sleep_stats"; | ||
| 5 | - static const String viewFriendSleepStats = "view_friend_sleep_stats"; | ||
| 6 | - | ||
| 7 | - static const Set<String> keys = { | ||
| 8 | - hrvChangeClick, | ||
| 9 | - friendHrvChangeClick, | ||
| 10 | - viewSleepStats, | ||
| 11 | - viewFriendSleepStats, | ||
| 12 | - }; | ||
| 13 | -} |
| 1 | -import 'package:doublefeel_flutter/data/models/event/click_event_data.dart'; | ||
| 2 | - | ||
| 3 | -import '../../result/app_result.dart'; | ||
| 4 | -import '../../result/safe_call.dart'; | ||
| 5 | -import '../api_paths.dart'; | ||
| 6 | -import '../dio_client.dart'; | ||
| 7 | - | ||
| 8 | -class ClickEventApi { | ||
| 9 | - ClickEventApi(this._dioClient); | ||
| 10 | - | ||
| 11 | - final DioClient _dioClient; | ||
| 12 | - | ||
| 13 | - Future<AppResult<void>> postClickEvent(String eventName) { | ||
| 14 | - return safeCall( | ||
| 15 | - call: () async { | ||
| 16 | - await _dioClient.dio.post( | ||
| 17 | - ApiPaths.clickEvent, | ||
| 18 | - data: {'event_name': eventName}, | ||
| 19 | - ); | ||
| 20 | - }, | ||
| 21 | - ); | ||
| 22 | - } | ||
| 23 | - | ||
| 24 | - Future<AppResult<ClickEventData>> getClickEvent() { | ||
| 25 | - return safeCall( | ||
| 26 | - call: () async { | ||
| 27 | - final response = await _dioClient.dio.get(ApiPaths.clickEvent); | ||
| 28 | - return ClickEventData.fromJson(response.data as Map<String, dynamic>); | ||
| 29 | - }, | ||
| 30 | - errorHandlingPolicy: null, | ||
| 31 | - ); | ||
| 32 | - } | ||
| 33 | -} |
| @@ -72,7 +72,4 @@ abstract final class ApiPaths { | @@ -72,7 +72,4 @@ abstract final class ApiPaths { | ||
| 72 | // friends | 72 | // friends |
| 73 | static const friends = '/client/doublefeel/health/v2/friends/'; | 73 | static const friends = '/client/doublefeel/health/v2/friends/'; |
| 74 | static const friendInfo = '/client/doublefeel/health/v2/friend_info/'; | 74 | static const friendInfo = '/client/doublefeel/health/v2/friend_info/'; |
| 75 | - | ||
| 76 | - // click event | ||
| 77 | - static const clickEvent = '/client/doublefeel/click_event/'; | ||
| 78 | } | 75 | } |
| 1 | -class ClickEventData { | ||
| 2 | - ClickEventData({ | ||
| 3 | - this.list, | ||
| 4 | - }); | ||
| 5 | - | ||
| 6 | - ClickEventData.fromJson(dynamic json) { | ||
| 7 | - if (json['list'] != null) { | ||
| 8 | - list = []; | ||
| 9 | - json['list'].forEach((v) { | ||
| 10 | - list?.add(ClickEventItem.fromJson(v)); | ||
| 11 | - }); | ||
| 12 | - } | ||
| 13 | - } | ||
| 14 | - | ||
| 15 | - List<ClickEventItem>? list; | ||
| 16 | - | ||
| 17 | - Map<String, dynamic> toJson() { | ||
| 18 | - final map = <String, dynamic>{}; | ||
| 19 | - if (list != null) { | ||
| 20 | - map['list'] = list?.map((v) => v.toJson()).toList(); | ||
| 21 | - } | ||
| 22 | - return map; | ||
| 23 | - } | ||
| 24 | -} | ||
| 25 | - | ||
| 26 | -class ClickEventItem { | ||
| 27 | - ClickEventItem({ | ||
| 28 | - this.eventName, | ||
| 29 | - this.clickCnt, | ||
| 30 | - }); | ||
| 31 | - | ||
| 32 | - ClickEventItem.fromJson(dynamic json) { | ||
| 33 | - eventName = json['event_name']; | ||
| 34 | - clickCnt = json['click_cnt']; | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - String? eventName; | ||
| 38 | - num? clickCnt; | ||
| 39 | - | ||
| 40 | - Map<String, dynamic> toJson() { | ||
| 41 | - final map = <String, dynamic>{}; | ||
| 42 | - map['event_name'] = eventName; | ||
| 43 | - map['click_cnt'] = clickCnt; | ||
| 44 | - return map; | ||
| 45 | - } | ||
| 46 | -} |
-
Please register or login to post a comment