Commit d1e329b8e0e29971c2e8ae2b1985cb6093b1a156

Authored by 刘宏哲
1 parent 30f20ce4

feat(app): 好评弹窗逻辑修改

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 ),
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 -}