Commit cba16e330b34d7a4791140f3a60d1ab19a9b80dd

Authored by 常守达
1 parent 747d84d7

fix(today):看板问题修复

@@ -202,7 +202,7 @@ class _ProfileHeader extends StatelessWidget { @@ -202,7 +202,7 @@ class _ProfileHeader extends StatelessWidget {
202 ), 202 ),
203 const SizedBox(height: 1), 203 const SizedBox(height: 1),
204 Text( 204 Text(
205 - 'ID:${user?.id ?? 0}', 205 + 'ID:${user?.uniqueCode ?? ''}',
206 maxLines: 1, 206 maxLines: 1,
207 overflow: TextOverflow.ellipsis, 207 overflow: TextOverflow.ellipsis,
208 style: TextStyle( 208 style: TextStyle(
@@ -239,12 +239,7 @@ class TodayTabBody extends StatelessWidget { @@ -239,12 +239,7 @@ class TodayTabBody extends StatelessWidget {
239 controller, 239 controller,
240 )); 240 ));
241 }), 241 }),
242 - Obx(() => controller.showHrvAdBanner.value  
243 - ? TodayHrvAdBanner(controller: controller)  
244 - : const SizedBox.shrink()),  
245 - Obx(() => controller.showPartnerAdBanner.value  
246 - ? TodayPartnerAdBanner(controller: controller)  
247 - : const SizedBox.shrink()), 242 + TodayAdCarousel(controller: controller),
248 TodayHrvNumberCard(controller: controller), 243 TodayHrvNumberCard(controller: controller),
249 const SizedBox(height: 12), 244 const SizedBox(height: 12),
250 TodayHrvChartCard(controller: controller), 245 TodayHrvChartCard(controller: controller),
@@ -286,7 +281,8 @@ class TodayTabBody extends StatelessWidget { @@ -286,7 +281,8 @@ class TodayTabBody extends StatelessWidget {
286 return GestureDetector( 281 return GestureDetector(
287 onTap: showHrvMeasurementBottomSheet, 282 onTap: showHrvMeasurementBottomSheet,
288 child: Container( 283 child: Container(
289 - padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), 284 + margin: EdgeInsets.only(top: 6),
  285 + padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
290 decoration: BoxDecoration( 286 decoration: BoxDecoration(
291 color: Colors.white, 287 color: Colors.white,
292 borderRadius: BorderRadius.circular(16), 288 borderRadius: BorderRadius.circular(16),
@@ -99,7 +99,7 @@ class DfTabBar extends StatelessWidget { @@ -99,7 +99,7 @@ class DfTabBar extends StatelessWidget {
99 height: 24, 99 height: 24,
100 gaplessPlayback: true, 100 gaplessPlayback: true,
101 ), 101 ),
102 - const SizedBox(height: 2), 102 + const SizedBox(height: 0.5),
103 Text( 103 Text(
104 tabLabels[i], 104 tabLabels[i],
105 style: TextStyle( 105 style: TextStyle(
  1 +import 'dart:async';
1 import 'package:doublefeel_flutter/app/routes/app_pages.dart'; 2 import 'package:doublefeel_flutter/app/routes/app_pages.dart';
2 import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; 3 import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
3 import 'package:doublefeel_flutter/core/theme/app_theme.dart'; 4 import 'package:doublefeel_flutter/core/theme/app_theme.dart';
@@ -8,9 +9,14 @@ import 'package:get/get.dart'; @@ -8,9 +9,14 @@ import 'package:get/get.dart';
8 import '../../controllers/today_controller.dart'; 9 import '../../controllers/today_controller.dart';
9 10
10 class TodayHrvAdBanner extends StatelessWidget { 11 class TodayHrvAdBanner extends StatelessWidget {
11 - const TodayHrvAdBanner({super.key, required this.controller}); 12 + const TodayHrvAdBanner({
  13 + super.key,
  14 + required this.controller,
  15 + this.showMargin = true,
  16 + });
12 17
13 final TodayController controller; 18 final TodayController controller;
  19 + final bool showMargin;
14 20
15 @override 21 @override
16 Widget build(BuildContext context) { 22 Widget build(BuildContext context) {
@@ -29,7 +35,7 @@ class TodayHrvAdBanner extends StatelessWidget { @@ -29,7 +35,7 @@ class TodayHrvAdBanner extends StatelessWidget {
29 subtitle: context.l10n.stayOnTopOfYourHealthFluctuations, 35 subtitle: context.l10n.stayOnTopOfYourHealthFluctuations,
30 right: Container( 36 right: Container(
31 padding: const EdgeInsets.all(4), 37 padding: const EdgeInsets.all(4),
32 - decoration: BoxDecoration( 38 + decoration: const BoxDecoration(
33 color: Colors.white, 39 color: Colors.white,
34 shape: BoxShape.circle, 40 shape: BoxShape.circle,
35 ), 41 ),
@@ -42,6 +48,7 @@ class TodayHrvAdBanner extends StatelessWidget { @@ -42,6 +48,7 @@ class TodayHrvAdBanner extends StatelessWidget {
42 ), 48 ),
43 leftImagePath: 'assets/images/today/ic_banner_watch_hrv_theme.png', 49 leftImagePath: 'assets/images/today/ic_banner_watch_hrv_theme.png',
44 onClose: controller.dismissHrvAdBanner, 50 onClose: controller.dismissHrvAdBanner,
  51 + showMargin: showMargin,
45 ), 52 ),
46 ); 53 );
47 }); 54 });
@@ -49,9 +56,14 @@ class TodayHrvAdBanner extends StatelessWidget { @@ -49,9 +56,14 @@ class TodayHrvAdBanner extends StatelessWidget {
49 } 56 }
50 57
51 class TodayPartnerAdBanner extends StatelessWidget { 58 class TodayPartnerAdBanner extends StatelessWidget {
52 - const TodayPartnerAdBanner({super.key, required this.controller}); 59 + const TodayPartnerAdBanner({
  60 + super.key,
  61 + required this.controller,
  62 + this.showMargin = true,
  63 + });
53 64
54 final TodayController controller; 65 final TodayController controller;
  66 + final bool showMargin;
55 67
56 @override 68 @override
57 Widget build(BuildContext context) { 69 Widget build(BuildContext context) {
@@ -92,6 +104,7 @@ class TodayPartnerAdBanner extends StatelessWidget { @@ -92,6 +104,7 @@ class TodayPartnerAdBanner extends StatelessWidget {
92 ), 104 ),
93 leftImagePath: 'assets/images/today/ic_banner_add_contact.png', 105 leftImagePath: 'assets/images/today/ic_banner_add_contact.png',
94 onClose: controller.dismissPartnerAdBanner, 106 onClose: controller.dismissPartnerAdBanner,
  107 + showMargin: showMargin,
95 ), 108 ),
96 ); 109 );
97 }); 110 });
@@ -105,6 +118,7 @@ class _TodayGuideBanner extends StatelessWidget { @@ -105,6 +118,7 @@ class _TodayGuideBanner extends StatelessWidget {
105 required this.onClose, 118 required this.onClose,
106 this.right, 119 this.right,
107 required this.leftImagePath, 120 required this.leftImagePath,
  121 + this.showMargin = true,
108 }); 122 });
109 123
110 final String title; 124 final String title;
@@ -112,6 +126,7 @@ class _TodayGuideBanner extends StatelessWidget { @@ -112,6 +126,7 @@ class _TodayGuideBanner extends StatelessWidget {
112 final VoidCallback onClose; 126 final VoidCallback onClose;
113 final Widget? right; 127 final Widget? right;
114 final String leftImagePath; 128 final String leftImagePath;
  129 + final bool showMargin;
115 130
116 @override 131 @override
117 Widget build(BuildContext context) { 132 Widget build(BuildContext context) {
@@ -119,7 +134,7 @@ class _TodayGuideBanner extends StatelessWidget { @@ -119,7 +134,7 @@ class _TodayGuideBanner extends StatelessWidget {
119 134
120 return Container( 135 return Container(
121 height: 65, 136 height: 65,
122 - margin: const EdgeInsets.only(bottom: 12), 137 + margin: showMargin ? const EdgeInsets.only(bottom: 12) : EdgeInsets.zero,
123 decoration: BoxDecoration( 138 decoration: BoxDecoration(
124 color: colors.brandBackgroundLight, 139 color: colors.brandBackgroundLight,
125 borderRadius: BorderRadius.circular(16), 140 borderRadius: BorderRadius.circular(16),
@@ -188,3 +203,137 @@ class _TodayGuideBanner extends StatelessWidget { @@ -188,3 +203,137 @@ class _TodayGuideBanner extends StatelessWidget {
188 ); 203 );
189 } 204 }
190 } 205 }
  206 +
  207 +class TodayAdCarousel extends StatelessWidget {
  208 + const TodayAdCarousel({super.key, required this.controller});
  209 +
  210 + final TodayController controller;
  211 +
  212 + @override
  213 + Widget build(BuildContext context) {
  214 + return Obx(() {
  215 + final showHrv = controller.showHrvAdBanner.value;
  216 + final showPartner = controller.showPartnerAdBanner.value &&
  217 + !controller.userStateService.isBound;
  218 +
  219 + final activeBanners = <Widget>[];
  220 + if (showHrv) {
  221 + activeBanners.add(
  222 + TodayHrvAdBanner(
  223 + controller: controller,
  224 + showMargin: false,
  225 + ),
  226 + );
  227 + }
  228 + if (showPartner) {
  229 + activeBanners.add(
  230 + TodayPartnerAdBanner(
  231 + controller: controller,
  232 + showMargin: false,
  233 + ),
  234 + );
  235 + }
  236 +
  237 + if (activeBanners.isEmpty) {
  238 + return const SizedBox.shrink();
  239 + }
  240 +
  241 + if (activeBanners.length == 1) {
  242 + return Padding(
  243 + padding: const EdgeInsets.only(bottom: 12),
  244 + child: activeBanners.first,
  245 + );
  246 + }
  247 +
  248 + return Container(
  249 + height: 65,
  250 + margin: const EdgeInsets.only(bottom: 12),
  251 + child: _BannerSlider(items: activeBanners),
  252 + );
  253 + });
  254 + }
  255 +}
  256 +
  257 +class _BannerSlider extends StatefulWidget {
  258 + const _BannerSlider({required this.items});
  259 + final List<Widget> items;
  260 +
  261 + @override
  262 + State<_BannerSlider> createState() => _BannerSliderState();
  263 +}
  264 +
  265 +class _BannerSliderState extends State<_BannerSlider> {
  266 + late PageController _pageController;
  267 + Timer? _autoScrollTimer;
  268 + bool _userScrolling = false;
  269 +
  270 + int get _realCount => widget.items.length;
  271 + int get _virtualCount => _realCount * 10000;
  272 + int get _initialPage => _virtualCount ~/ 2;
  273 +
  274 + @override
  275 + void initState() {
  276 + super.initState();
  277 + _pageController = PageController(initialPage: _initialPage);
  278 + WidgetsBinding.instance.addPostFrameCallback((_) {
  279 + if (mounted) {
  280 + _startAutoScroll();
  281 + }
  282 + });
  283 + }
  284 +
  285 + void _startAutoScroll() {
  286 + _autoScrollTimer?.cancel();
  287 + _autoScrollTimer = Timer.periodic(const Duration(seconds: 4), (_) {
  288 + if (!mounted || _userScrolling) return;
  289 + if (_pageController.hasClients) {
  290 + final next = (_pageController.page?.round() ?? _initialPage) + 1;
  291 + _pageController.animateToPage(
  292 + next,
  293 + duration: const Duration(milliseconds: 500),
  294 + curve: Curves.easeInOut,
  295 + );
  296 + }
  297 + });
  298 + }
  299 +
  300 + void _stopAutoScroll() {
  301 + _autoScrollTimer?.cancel();
  302 + _autoScrollTimer = null;
  303 + }
  304 +
  305 + @override
  306 + void didUpdateWidget(covariant _BannerSlider oldWidget) {
  307 + super.didUpdateWidget(oldWidget);
  308 + if (widget.items.length != oldWidget.items.length) {
  309 + _stopAutoScroll();
  310 + _pageController.dispose();
  311 + _pageController = PageController(initialPage: _initialPage);
  312 + _startAutoScroll();
  313 + }
  314 + }
  315 +
  316 + @override
  317 + void dispose() {
  318 + _stopAutoScroll();
  319 + _pageController.dispose();
  320 + super.dispose();
  321 + }
  322 +
  323 + @override
  324 + Widget build(BuildContext context) {
  325 + return Listener(
  326 + onPointerDown: (_) => _userScrolling = true,
  327 + onPointerUp: (_) => _userScrolling = false,
  328 + onPointerCancel: (_) => _userScrolling = false,
  329 + child: PageView.builder(
  330 + controller: _pageController,
  331 + itemCount: _virtualCount,
  332 + itemBuilder: (context, index) {
  333 + final realIndex = index % _realCount;
  334 + return widget.items[realIndex];
  335 + },
  336 + ),
  337 + );
  338 + }
  339 +}
@@ -7,6 +7,7 @@ import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; @@ -7,6 +7,7 @@ import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
7 import 'package:fl_chart/fl_chart.dart'; 7 import 'package:fl_chart/fl_chart.dart';
8 import 'package:flutter/material.dart'; 8 import 'package:flutter/material.dart';
9 import 'package:get/get.dart'; 9 import 'package:get/get.dart';
  10 +import 'package:lottie/lottie.dart';
10 11
11 import '../../controllers/today_controller.dart'; 12 import '../../controllers/today_controller.dart';
12 import 'hrv_principle_explanation_bottom_sheet.dart'; 13 import 'hrv_principle_explanation_bottom_sheet.dart';
@@ -47,7 +48,7 @@ class TodayHrvChartCard extends StatelessWidget { @@ -47,7 +48,7 @@ class TodayHrvChartCard extends StatelessWidget {
47 }; 48 };
48 49
49 return Container( 50 return Container(
50 - padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), 51 + padding: const EdgeInsets.fromLTRB(20, 16, 20, 16),
51 decoration: BoxDecoration( 52 decoration: BoxDecoration(
52 color: Colors.white, 53 color: Colors.white,
53 borderRadius: BorderRadius.circular(16), 54 borderRadius: BorderRadius.circular(16),
@@ -59,16 +60,26 @@ class TodayHrvChartCard extends StatelessWidget { @@ -59,16 +60,26 @@ class TodayHrvChartCard extends StatelessWidget {
59 const SizedBox(height: 14), 60 const SizedBox(height: 14),
60 SizedBox( 61 SizedBox(
61 height: 174, 62 height: 174,
62 - child: hrvSpots.isEmpty  
63 - ? _EmptyChart(text: context.l10n.noDataAvailableForToday)  
64 - : _HrvLineChart(  
65 - spots: hrvSpots,  
66 - stateMap: hrvStateMap,  
67 - maxSeconds: _maxChartSeconds(),  
68 - getColorByState: _getColorByState,  
69 - getTooltipChildren: _getTooltipChildren,  
70 - timeLabel: _timeLabel,  
71 - ), 63 + child: controller.isLoadingToday.value
  64 + ? Center(
  65 + child: Lottie.asset(
  66 + 'assets/lottie/loading.json',
  67 + width: 130,
  68 + height: 90,
  69 + repeat: true,
  70 + animate: true,
  71 + ),
  72 + )
  73 + : hrvSpots.isEmpty
  74 + ? _EmptyChart(text: context.l10n.noDataAvailableForToday)
  75 + : _HrvLineChart(
  76 + spots: hrvSpots,
  77 + stateMap: hrvStateMap,
  78 + maxSeconds: _maxChartSeconds(),
  79 + getColorByState: _getColorByState,
  80 + getTooltipChildren: _getTooltipChildren,
  81 + timeLabel: _timeLabel,
  82 + ),
72 ), 83 ),
73 Padding( 84 Padding(
74 padding: const EdgeInsets.only(bottom: 14, top: 16), 85 padding: const EdgeInsets.only(bottom: 14, top: 16),
@@ -116,13 +127,23 @@ class TodayHrvChartCard extends StatelessWidget { @@ -116,13 +127,23 @@ class TodayHrvChartCard extends StatelessWidget {
116 ), 127 ),
117 SizedBox( 128 SizedBox(
118 height: 190, 129 height: 190,
119 - child: _buildStressChart(  
120 - context,  
121 - sleepList,  
122 - stressPoints,  
123 - selectedDate,  
124 - vipInfo?.isVip == true,  
125 - ), 130 + child: controller.isLoadingToday.value
  131 + ? Center(
  132 + child: Lottie.asset(
  133 + 'assets/lottie/loading.json',
  134 + width: 130,
  135 + height: 90,
  136 + repeat: true,
  137 + animate: true,
  138 + ),
  139 + )
  140 + : _buildStressChart(
  141 + context,
  142 + sleepList,
  143 + stressPoints,
  144 + selectedDate,
  145 + vipInfo?.isVip == true,
  146 + ),
126 ), 147 ),
127 ], 148 ],
128 ), 149 ),
@@ -139,14 +160,14 @@ class TodayHrvChartCard extends StatelessWidget { @@ -139,14 +160,14 @@ class TodayHrvChartCard extends StatelessWidget {
139 Builder(builder: (context) { 160 Builder(builder: (context) {
140 final s = (sameDay 161 final s = (sameDay
141 ? context.l10n.todaySHrvTrend 162 ? context.l10n.todaySHrvTrend
142 - : context.l10n.averageHrvForTheDay); 163 + : context.l10n.hrvTrendForThatDay);
143 return Text( 164 return Text(
144 (controller.isFriend 165 (controller.isFriend
145 ? context.l10n.reportOtherPossessiveTitle(s) 166 ? context.l10n.reportOtherPossessiveTitle(s)
146 : s), 167 : s),
147 style: TextStyle( 168 style: TextStyle(
148 color: context.colors.textPrimary, 169 color: context.colors.textPrimary,
149 - fontSize: 16, 170 + fontSize: 18,
150 fontWeight: FontWeight.w600, 171 fontWeight: FontWeight.w600,
151 ), 172 ),
152 ); 173 );
@@ -363,7 +384,7 @@ class TodayHrvChartCard extends StatelessWidget { @@ -363,7 +384,7 @@ class TodayHrvChartCard extends StatelessWidget {
363 List<TextSpan>? _getTooltip(BarChartRodData rod, int x, int? state) { 384 List<TextSpan>? _getTooltip(BarChartRodData rod, int x, int? state) {
364 return [ 385 return [
365 TextSpan( 386 TextSpan(
366 - text: '○ ${_getStatusByState(state)}', 387 + text: '○ ${_getStatusByState(state)} ${_formatNumber(rod.toY)}%',
367 style: TextStyle( 388 style: TextStyle(
368 color: _getColorByState(state), 389 color: _getColorByState(state),
369 fontWeight: FontWeight.bold, 390 fontWeight: FontWeight.bold,
@@ -377,8 +398,7 @@ class TodayHrvChartCard extends StatelessWidget { @@ -377,8 +398,7 @@ class TodayHrvChartCard extends StatelessWidget {
377 ), 398 ),
378 ), 399 ),
379 TextSpan( 400 TextSpan(
380 - text:  
381 - '♥ ${l10n.pressure} ${_formatNumber(rod.toY)} · ${_formatHour(x.toDouble())}', 401 + text: '♥ ${l10n.pressure} ${_formatHour(x.toDouble())}',
382 style: const TextStyle( 402 style: const TextStyle(
383 color: Color(0xFF78787D), 403 color: Color(0xFF78787D),
384 fontWeight: FontWeight.bold, 404 fontWeight: FontWeight.bold,
@@ -510,13 +530,18 @@ class _HrvLineChartState extends State<_HrvLineChart> { @@ -510,13 +530,18 @@ class _HrvLineChartState extends State<_HrvLineChart> {
510 showTitles: true, 530 showTitles: true,
511 reservedSize: 20, 531 reservedSize: 20,
512 interval: 6 * 3600, 532 interval: 6 * 3600,
513 - getTitlesWidget: (val, _) { 533 + getTitlesWidget: (val, meta) {
514 final label = widget.timeLabel(val); 534 final label = widget.timeLabel(val);
515 if (label == null) return const SizedBox.shrink(); 535 if (label == null) return const SizedBox.shrink();
516 - return Text(  
517 - label,  
518 - style: TextStyle(  
519 - fontSize: 10, color: context.colors.textTertiary), 536 + return SideTitleWidget(
  537 + meta: meta,
  538 + fitInside: SideTitleFitInsideData.fromTitleMeta(meta,
  539 + distanceFromEdge: 0),
  540 + child: Text(
  541 + label,
  542 + style: TextStyle(
  543 + fontSize: 10, color: context.colors.textTertiary),
  544 + ),
520 ); 545 );
521 }, 546 },
522 ), 547 ),
@@ -679,12 +704,11 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -679,12 +704,11 @@ class _StressBarChartState extends State<_StressBarChart> {
679 const rightAxisWidth = 22.0; 704 const rightAxisWidth = 22.0;
680 const slotSec = 6 * 60; 705 const slotSec = 6 * 60;
681 final slotCount = (widget.maxSeconds / slotSec).ceil().clamp(1, 9999); 706 final slotCount = (widget.maxSeconds / slotSec).ceil().clamp(1, 9999);
682 - const double groupsSpace = 0.5; // 设置间隔为 1 像素 707 + const double groupsSpace = 0; // 设置间隔为 1 像素
683 final barWidth = (constraints.maxWidth - 708 final barWidth = (constraints.maxWidth -
684 rightAxisWidth - 709 rightAxisWidth -
685 (groupsSpace * (slotCount - 1))) / 710 (groupsSpace * (slotCount - 1))) /
686 slotCount; 711 slotCount;
687 -  
688 // 根据 V2SleepTimeRange 计算多段睡眠区间的 Positioned widgets 712 // 根据 V2SleepTimeRange 计算多段睡眠区间的 Positioned widgets
689 final sleepWidgets = <Widget>[]; 713 final sleepWidgets = <Widget>[];
690 714
@@ -866,7 +890,7 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -866,7 +890,7 @@ class _StressBarChartState extends State<_StressBarChart> {
866 sideTitles: SideTitles( 890 sideTitles: SideTitles(
867 showTitles: true, 891 showTitles: true,
868 reservedSize: 22, 892 reservedSize: 22,
869 - interval: 60, 893 + interval: 6 * 3600,
870 getTitlesWidget: (val, meta) { 894 getTitlesWidget: (val, meta) {
871 final label = widget.timeLabel(val); 895 final label = widget.timeLabel(val);
872 if (label == null) { 896 if (label == null) {
@@ -874,6 +898,9 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -874,6 +898,9 @@ class _StressBarChartState extends State<_StressBarChart> {
874 } 898 }
875 return SideTitleWidget( 899 return SideTitleWidget(
876 meta: meta, 900 meta: meta,
  901 + fitInside: SideTitleFitInsideData.fromTitleMeta(
  902 + meta,
  903 + distanceFromEdge: 0),
877 child: Text( 904 child: Text(
878 label, 905 label,
879 style: TextStyle( 906 style: TextStyle(
@@ -932,7 +959,9 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -932,7 +959,9 @@ class _StressBarChartState extends State<_StressBarChart> {
932 bottom: 5, 959 bottom: 5,
933 ), 960 ),
934 getTooltipItem: (group, groupIndex, rod, rodIndex) { 961 getTooltipItem: (group, groupIndex, rod, rodIndex) {
935 - final state = slotStateMap[group.x]?.state; 962 + final entry = slotStateMap[group.x];
  963 + if (entry == null) return null;
  964 + final state = entry.state;
936 return BarTooltipItem( 965 return BarTooltipItem(
937 '', 966 '',
938 const TextStyle( 967 const TextStyle(
@@ -48,8 +48,22 @@ class LoginController extends GetxController { @@ -48,8 +48,22 @@ class LoginController extends GetxController {
48 48
49 AppleSignInModel? appleSignInModel; 49 AppleSignInModel? appleSignInModel;
50 50
51 - void toggleTermsChecked() {  
52 - termsChecked.value = !termsChecked.value; 51 + Future<void> toggleTermsChecked() async {
  52 + final newValue = !termsChecked.value;
  53 + termsChecked.value = newValue;
  54 + try {
  55 + final local = Get.find<LocalStorage>();
  56 + await local.setTermsAgreed(newValue);
  57 + if (newValue && !_userStateService.sdksInitialized) {
  58 + await _userStateService.initializeSdks();
  59 + }
  60 + if (needTrackWelcomepage) {
  61 + ta.track('enter_doublefeel_welcome_page');
  62 + needTrackWelcomepage = false;
  63 + }
  64 + } catch (e) {
  65 + AppLogger.e(e.toString());
  66 + }
53 } 67 }
54 68
55 void onPhoneLoginPressed() { 69 void onPhoneLoginPressed() {
@@ -104,6 +118,10 @@ class LoginController extends GetxController { @@ -104,6 +118,10 @@ class LoginController extends GetxController {
104 @override 118 @override
105 void onInit() { 119 void onInit() {
106 super.onInit(); 120 super.onInit();
  121 + LocalStorage.open().then((local) {
  122 + termsChecked.value = local.termsAgreed;
  123 + });
  124 +
107 phoneController.addListener(() { 125 phoneController.addListener(() {
108 phoneInput.value = phoneController.text; 126 phoneInput.value = phoneController.text;
109 }); 127 });
@@ -124,9 +142,15 @@ class LoginController extends GetxController { @@ -124,9 +142,15 @@ class LoginController extends GetxController {
124 @override 142 @override
125 void onReady() { 143 void onReady() {
126 super.onReady(); 144 super.onReady();
127 - ta.track('enter_doublefeel_welcome_page'); 145 + if (_userStateService.sdksInitialized) {
  146 + ta.track('enter_doublefeel_welcome_page');
  147 + } else {
  148 + needTrackWelcomepage = true;
  149 + }
128 } 150 }
129 151
  152 + bool needTrackWelcomepage = false;
  153 +
130 String get cleanPhone => phoneInput.value.replaceAll(' ', ''); 154 String get cleanPhone => phoneInput.value.replaceAll(' ', '');
131 155
132 bool get canRequestCode => 156 bool get canRequestCode =>
@@ -21,10 +21,14 @@ class UserStateService { @@ -21,10 +21,14 @@ class UserStateService {
21 final PushService _pushService; 21 final PushService _pushService;
22 final ThinkingDataService _thinkingDataService; 22 final ThinkingDataService _thinkingDataService;
23 23
  24 + bool sdksInitialized = false;
  25 +
24 /// Called after the user accepts terms (cold start, not logged in yet). 26 /// Called after the user accepts terms (cold start, not logged in yet).
25 Future<void> initializeSdks() async { 27 Future<void> initializeSdks() async {
  28 + if (sdksInitialized) return;
26 await _thinkingDataService.init(); 29 await _thinkingDataService.init();
27 await _imService.init(); 30 await _imService.init();
  31 + sdksInitialized = true;
28 } 32 }
29 33
30 Future<void> onLogin() async { 34 Future<void> onLogin() async {
@@ -561,5 +561,6 @@ @@ -561,5 +561,6 @@
561 }, 561 },
562 "freeRedemptionOffer": "Free Redemption Offer", 562 "freeRedemptionOffer": "Free Redemption Offer",
563 "cellPhoneNumber": "Cell phone number", 563 "cellPhoneNumber": "Cell phone number",
564 - "todayOnWeeklyCalendar": "today" 564 + "todayOnWeeklyCalendar": "today",
  565 + "hrvTrendForThatDay": "HRV Trend for That Day"
565 } 566 }
@@ -849,5 +849,6 @@ @@ -849,5 +849,6 @@
849 }, 849 },
850 "freeRedemptionOffer": "免费兑换优惠", 850 "freeRedemptionOffer": "免费兑换优惠",
851 "cellPhoneNumber": "手机号", 851 "cellPhoneNumber": "手机号",
852 - "todayOnWeeklyCalendar": "今" 852 + "todayOnWeeklyCalendar": "今",
  853 + "hrvTrendForThatDay": "该日HRV趋势"
853 } 854 }
@@ -3326,6 +3326,12 @@ abstract class AppLocalizations { @@ -3326,6 +3326,12 @@ abstract class AppLocalizations {
3326 /// In zh, this message translates to: 3326 /// In zh, this message translates to:
3327 /// **'今'** 3327 /// **'今'**
3328 String get todayOnWeeklyCalendar; 3328 String get todayOnWeeklyCalendar;
  3329 +
  3330 + /// No description provided for @hrvTrendForThatDay.
  3331 + ///
  3332 + /// In zh, this message translates to:
  3333 + /// **'该日HRV趋势'**
  3334 + String get hrvTrendForThatDay;
3329 } 3335 }
3330 3336
3331 class _AppLocalizationsDelegate 3337 class _AppLocalizationsDelegate
@@ -1853,4 +1853,7 @@ class AppLocalizationsEn extends AppLocalizations { @@ -1853,4 +1853,7 @@ class AppLocalizationsEn extends AppLocalizations {
1853 1853
1854 @override 1854 @override
1855 String get todayOnWeeklyCalendar => 'today'; 1855 String get todayOnWeeklyCalendar => 'today';
  1856 +
  1857 + @override
  1858 + String get hrvTrendForThatDay => 'HRV Trend for That Day';
1856 } 1859 }
@@ -1754,4 +1754,7 @@ class AppLocalizationsZh extends AppLocalizations { @@ -1754,4 +1754,7 @@ class AppLocalizationsZh extends AppLocalizations {
1754 1754
1755 @override 1755 @override
1756 String get todayOnWeeklyCalendar => '今'; 1756 String get todayOnWeeklyCalendar => '今';
  1757 +
  1758 + @override
  1759 + String get hrvTrendForThatDay => '该日HRV趋势';
1757 } 1760 }