Commit e76e2067555a8fbe68301dd953d5cf0330695552

Authored by 常守达
1 parent 4967db0c

fix(today):图标tooltip,分享链接

@@ -213,11 +213,11 @@ class TodayController extends GetMaterialController { @@ -213,11 +213,11 @@ class TodayController extends GetMaterialController {
213 213
214 void changeDate(DateTime date, {DateTime? focused}) { 214 void changeDate(DateTime date, {DateTime? focused}) {
215 final normalizedDate = _clampDate(date); 215 final normalizedDate = _clampDate(date);
216 - // if (!DateUtils.isSameDay(date, lastSelectableDay.value) &&  
217 - // !_userStateService.isVip) {  
218 - // toPremiumPage('今日-查看非当日数据');  
219 - // return;  
220 - // } 216 + if (!DateUtils.isSameDay(date, lastSelectableDay.value) &&
  217 + !_userStateService.isVip) {
  218 + toPremiumPage('今日-查看非当日数据');
  219 + return;
  220 + }
221 selectedDate.value = normalizedDate; 221 selectedDate.value = normalizedDate;
222 focusedDay.value = _clampDate(focused ?? normalizedDate); 222 focusedDay.value = _clampDate(focused ?? normalizedDate);
223 } 223 }
@@ -339,6 +339,7 @@ class TodayController extends GetMaterialController { @@ -339,6 +339,7 @@ class TodayController extends GetMaterialController {
339 }, 339 },
340 ); 340 );
341 } else { 341 } else {
  342 + unawaited(loadDataForDate(selectedDate.value));
342 await checkUploadedStatus(firstTime, result); 343 await checkUploadedStatus(firstTime, result);
343 } 344 }
344 } catch (e) {} 345 } catch (e) {}
@@ -453,7 +454,7 @@ class TodayController extends GetMaterialController { @@ -453,7 +454,7 @@ class TodayController extends GetMaterialController {
453 hrvAnnotations.clear(); 454 hrvAnnotations.clear();
454 455
455 v2HealthData.value = null; 456 v2HealthData.value = null;
456 - v2StressScore.value = null; 457 + // v2StressScore.value = null;
457 } 458 }
458 459
459 toTrendHrvPage() { 460 toTrendHrvPage() {
@@ -646,7 +647,7 @@ class TodayController extends GetMaterialController { @@ -646,7 +647,7 @@ class TodayController extends GetMaterialController {
646 checkAddFriendVisible(); 647 checkAddFriendVisible();
647 checkHrvAdBannerVisible(); 648 checkHrvAdBannerVisible();
648 checkHealthDataAuthCardVisible(); 649 checkHealthDataAuthCardVisible();
649 - unawaited(loadDataForDate(selectedDate.value)); 650 + // unawaited(loadDataForDate(selectedDate.value));
650 } 651 }
651 } 652 }
652 653
@@ -660,6 +661,6 @@ class TodayController extends GetMaterialController { @@ -660,6 +661,6 @@ class TodayController extends GetMaterialController {
660 checkAddFriendVisible(); 661 checkAddFriendVisible();
661 checkHrvAdBannerVisible(); 662 checkHrvAdBannerVisible();
662 checkHealthDataAuthCardVisible(); 663 checkHealthDataAuthCardVisible();
663 - await loadDataForDate(selectedDate.value); 664 + // await loadDataForDate(selectedDate.value);
664 } 665 }
665 } 666 }
@@ -37,12 +37,9 @@ class MyTab extends GetView<MyController> { @@ -37,12 +37,9 @@ class MyTab extends GetView<MyController> {
37 37
38 return ListView( 38 return ListView(
39 physics: const ClampingScrollPhysics(), 39 physics: const ClampingScrollPhysics(),
40 - padding: EdgeInsets.fromLTRB(  
41 - 16,  
42 - 59,  
43 - 16,  
44 - 150 + MediaQuery.paddingOf(context).bottom,  
45 - ), 40 + padding: EdgeInsets.fromLTRB(16, 59, 16, 0
  41 + // 150 + MediaQuery.paddingOf(context).bottom,
  42 + ),
46 children: [ 43 children: [
47 Padding( 44 Padding(
48 padding: const EdgeInsets.only(left: 8), 45 padding: const EdgeInsets.only(left: 8),
@@ -68,16 +65,16 @@ class MyTab extends GetView<MyController> { @@ -68,16 +65,16 @@ class MyTab extends GetView<MyController> {
68 title: context.l10n.help, 65 title: context.l10n.help,
69 onTap: () => Get.toNamed(Routes.HELP), 66 onTap: () => Get.toNamed(Routes.HELP),
70 ), 67 ),
71 - const SizedBox(height: 12),  
72 - _SettingsRow(  
73 - title: 'Apple Health Upload 测试',  
74 - onTap: () {  
75 - controller.testAppleHealthUpload();  
76 - },  
77 - ),  
78 if (environmentConfig.isDebug) ...[ 68 if (environmentConfig.isDebug) ...[
79 const SizedBox(height: 12), 69 const SizedBox(height: 12),
80 _SettingsRow( 70 _SettingsRow(
  71 + title: 'Apple Health Upload 测试',
  72 + onTap: () {
  73 + controller.testAppleHealthUpload();
  74 + },
  75 + ),
  76 + const SizedBox(height: 12),
  77 + _SettingsRow(
81 title: 'Developer options', 78 title: 'Developer options',
82 onTap: () { 79 onTap: () {
83 Get.toNamed(Routes.DEVELOPER_OPTIONS); 80 Get.toNamed(Routes.DEVELOPER_OPTIONS);
@@ -141,7 +141,8 @@ class TodayTabBody extends StatelessWidget { @@ -141,7 +141,8 @@ class TodayTabBody extends StatelessWidget {
141 child: CircularProgressIndicator()), 141 child: CircularProgressIndicator()),
142 ) 142 )
143 : Image.asset( 143 : Image.asset(
144 - controller.v2StressScore.value!.stateBg(), 144 + controller.v2StressScore.value?.stateBg() ??
  145 + 'assets/images/today/bg_stress_state_0.png',
145 fit: BoxFit.cover, 146 fit: BoxFit.cover,
146 width: Get.width, 147 width: Get.width,
147 height: Get.width / 375 * 345, 148 height: Get.width / 375 * 345,
@@ -515,6 +515,15 @@ class _HrvLineChartState extends State<_HrvLineChart> { @@ -515,6 +515,15 @@ class _HrvLineChartState extends State<_HrvLineChart> {
515 Timer? _hrvTooltipTimer; 515 Timer? _hrvTooltipTimer;
516 516
517 @override 517 @override
  518 + void didUpdateWidget(covariant _HrvLineChart oldWidget) {
  519 + super.didUpdateWidget(oldWidget);
  520 + if (widget.spots != oldWidget.spots) {
  521 + _hrvTooltipTimer?.cancel();
  522 + _hrvShowingSpots = [];
  523 + }
  524 + }
  525 +
  526 + @override
518 void dispose() { 527 void dispose() {
519 _hrvTooltipTimer?.cancel(); 528 _hrvTooltipTimer?.cancel();
520 super.dispose(); 529 super.dispose();
@@ -614,6 +623,8 @@ class _HrvLineChartState extends State<_HrvLineChart> { @@ -614,6 +623,8 @@ class _HrvLineChartState extends State<_HrvLineChart> {
614 ShowingTooltipIndicators(_hrvShowingSpots), 623 ShowingTooltipIndicators(_hrvShowingSpots),
615 ], 624 ],
616 lineTouchData: LineTouchData( 625 lineTouchData: LineTouchData(
  626 + getTouchLineStart: (barData, spotIndex) => 0,
  627 + getTouchLineEnd: (barData, spotIndex) => 90,
617 handleBuiltInTouches: false, 628 handleBuiltInTouches: false,
618 touchSpotThreshold: 20.0, 629 touchSpotThreshold: 20.0,
619 touchCallback: 630 touchCallback:
@@ -673,6 +684,8 @@ class _HrvLineChartState extends State<_HrvLineChart> { @@ -673,6 +684,8 @@ class _HrvLineChartState extends State<_HrvLineChart> {
673 }, 684 },
674 touchTooltipData: LineTouchTooltipData( 685 touchTooltipData: LineTouchTooltipData(
675 tooltipRoundedRadius: 8, 686 tooltipRoundedRadius: 8,
  687 + showOnTopOfTheChartBoxArea: true,
  688 + tooltipMargin: 6,
676 tooltipBorder: BorderSide.none, 689 tooltipBorder: BorderSide.none,
677 maxContentWidth: 400, 690 maxContentWidth: 400,
678 fitInsideHorizontally: true, 691 fitInsideHorizontally: true,
@@ -737,6 +750,18 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -737,6 +750,18 @@ class _StressBarChartState extends State<_StressBarChart> {
737 Timer? _stressTooltipTimer; 750 Timer? _stressTooltipTimer;
738 751
739 @override 752 @override
  753 + void didUpdateWidget(covariant _StressBarChart oldWidget) {
  754 + super.didUpdateWidget(oldWidget);
  755 + if (widget.selectedDate != oldWidget.selectedDate ||
  756 + widget.stressPoints != oldWidget.stressPoints) {
  757 + _stressTooltipTimer?.cancel();
  758 + setState(() {
  759 + _stressTouchedGroupIndex = null;
  760 + });
  761 + }
  762 + }
  763 +
  764 + @override
740 void dispose() { 765 void dispose() {
741 _stressTooltipTimer?.cancel(); 766 _stressTooltipTimer?.cancel();
742 super.dispose(); 767 super.dispose();
@@ -749,12 +774,14 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -749,12 +774,14 @@ class _StressBarChartState extends State<_StressBarChart> {
749 // 右轴 reservedSize=22,不占用空间设为 0.0 774 // 右轴 reservedSize=22,不占用空间设为 0.0
750 const rightAxisWidth = 0.0; 775 const rightAxisWidth = 0.0;
751 const slotSec = 6 * 60; 776 const slotSec = 6 * 60;
752 - final slotCount = (widget.maxSeconds / slotSec).ceil().clamp(1, 9999); 777 + // 关键:由于循环条件是 sec <= maxSec,实际绘制的组数(groups.length)为总槽数 + 1
  778 + final slotCount = (widget.maxSeconds / slotSec).round() + 1;
753 const double groupsSpace = 0.25; // 设置间隔为 1 像素 779 const double groupsSpace = 0.25; // 设置间隔为 1 像素
754 - final barWidth = (constraints.maxWidth -  
755 - rightAxisWidth -  
756 - (groupsSpace * (slotCount - 1))) /  
757 - slotCount; 780 + // 因为 BarChart 外层 Container 有 padding: EdgeInsets.only(right: 3),所以其实际宽度减少了 3.0
  781 + final chartWidth = constraints.maxWidth - 3.0;
  782 + final barWidth =
  783 + (chartWidth - rightAxisWidth - (groupsSpace * (slotCount - 1))) /
  784 + slotCount;
758 // 根据 V2SleepTimeRange 计算多段睡眠区间的 Positioned widgets 785 // 根据 V2SleepTimeRange 计算多段睡眠区间的 Positioned widgets
759 final sleepWidgets = <Widget>[]; 786 final sleepWidgets = <Widget>[];
760 787
@@ -873,6 +900,25 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -873,6 +900,25 @@ class _StressBarChartState extends State<_StressBarChart> {
873 final gridBottom = chartHeight - 20.0; 900 final gridBottom = chartHeight - 20.0;
874 final gridHeight = gridBottom - gridTop; 901 final gridHeight = gridBottom - gridTop;
875 902
  903 + // 自定义指示线 (只在 Stack 层手绘一条垂直灰色指示线,与柱子对齐,高度到达顶部以与 tooltip 连上)
  904 + Positioned? customIndicator;
  905 + if (_stressTouchedGroupIndex != null) {
  906 + final stepWidth = barWidth + groupsSpace;
  907 + final centerOfBar =
  908 + _stressTouchedGroupIndex! * stepWidth + (barWidth / 2);
  909 + customIndicator = Positioned(
  910 + left: centerOfBar - 1.0, // 2px 宽度,居中
  911 + width: 2.0,
  912 + top: 16.0, // 向上延展以连接 tooltip 底部
  913 + bottom: chartHeight - gridBottom, // 修正:bottom 指的是距离 Stack 底部的像素值
  914 + child: IgnorePointer(
  915 + child: ColoredBox(
  916 + color: context.colors.textTertiary.withAlpha(125),
  917 + ),
  918 + ),
  919 + );
  920 + }
  921 +
876 return Stack( 922 return Stack(
877 clipBehavior: Clip.none, 923 clipBehavior: Clip.none,
878 children: [ 924 children: [
@@ -1039,7 +1085,9 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -1039,7 +1085,9 @@ class _StressBarChartState extends State<_StressBarChart> {
1039 tooltipBorder: BorderSide.none, 1085 tooltipBorder: BorderSide.none,
1040 maxContentWidth: 400, 1086 maxContentWidth: 400,
1041 fitInsideHorizontally: true, 1087 fitInsideHorizontally: true,
1042 - fitInsideVertically: false, 1088 + fitInsideVertically:
  1089 + false, // 允许 tooltip 超出 BarChart 顶边界(绘制在 18px 的 top margin 区域内)
  1090 + tooltipMargin: 6, // 调整为和折线图一样的 6 像素空隙
1043 getTooltipColor: (touchedSpot) => const Color(0xFFF3F3F3), 1091 getTooltipColor: (touchedSpot) => const Color(0xFFF3F3F3),
1044 tooltipPadding: const EdgeInsets.only( 1092 tooltipPadding: const EdgeInsets.only(
1045 left: 12, 1093 left: 12,
@@ -1057,7 +1105,8 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -1057,7 +1105,8 @@ class _StressBarChartState extends State<_StressBarChart> {
1057 color: Colors.white, 1105 color: Colors.white,
1058 fontWeight: FontWeight.bold, 1106 fontWeight: FontWeight.bold,
1059 ), 1107 ),
1060 - children: widget.getTooltip(rod, group.x, state), 1108 + children: widget.getTooltip(
  1109 + group.barRods[0], group.x, state),
1061 textAlign: TextAlign.start, 1110 textAlign: TextAlign.start,
1062 ); 1111 );
1063 }, 1112 },
@@ -1066,6 +1115,8 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -1066,6 +1115,8 @@ class _StressBarChartState extends State<_StressBarChart> {
1066 ), 1115 ),
1067 ), 1116 ),
1068 ), 1117 ),
  1118 + if (customIndicator != null)
  1119 + customIndicator, // 将指示线移动到 Container (BarChart) 之后绘制,防止被盖住
1069 ...sleepWidgets, 1120 ...sleepWidgets,
1070 // 手绘 100、50、0 标签在对应的横线上方 1121 // 手绘 100、50、0 标签在对应的横线上方
1071 Positioned( 1122 Positioned(
@@ -1126,7 +1177,8 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -1126,7 +1177,8 @@ class _StressBarChartState extends State<_StressBarChart> {
1126 final isTouched = groupIndex == _stressTouchedGroupIndex; 1177 final isTouched = groupIndex == _stressTouchedGroupIndex;
1127 groups.add(BarChartGroupData( 1178 groups.add(BarChartGroupData(
1128 x: sec, 1179 x: sec,
1129 - showingTooltipIndicators: isTouched ? [0] : [], 1180 + barsSpace: 0,
  1181 + showingTooltipIndicators: isTouched ? [1] : [],
1130 barRods: [ 1182 barRods: [
1131 BarChartRodData( 1183 BarChartRodData(
1132 toY: entry != null ? entry.value.toDouble() : 0, 1184 toY: entry != null ? entry.value.toDouble() : 0,
@@ -1136,6 +1188,11 @@ class _StressBarChartState extends State<_StressBarChart> { @@ -1136,6 +1188,11 @@ class _StressBarChartState extends State<_StressBarChart> {
1136 : Colors.transparent, 1188 : Colors.transparent,
1137 borderRadius: BorderRadius.circular(barWidth / 2), 1189 borderRadius: BorderRadius.circular(barWidth / 2),
1138 ), 1190 ),
  1191 + BarChartRodData(
  1192 + toY: 100, // 用于固定 tooltip 在顶部
  1193 + width: 0, // 0 宽度,不占空间
  1194 + color: Colors.transparent,
  1195 + ),
1139 ], 1196 ],
1140 )); 1197 ));
1141 } 1198 }
@@ -551,7 +551,7 @@ @@ -551,7 +551,7 @@
551 "filming": "Filming", 551 "filming": "Filming",
552 "unlockTheProVersion": "Unlock the Pro Version", 552 "unlockTheProVersion": "Unlock the Pro Version",
553 "embarkOnAJourneyOfStressAwarenessAndWellnessSupport": "Embark on a Journey of Stress Awareness and Wellness Support", 553 "embarkOnAJourneyOfStressAwarenessAndWellnessSupport": "Embark on a Journey of Stress Awareness and Wellness Support",
554 - "sharePartnerCodeTemplate": "Hey ❤️ I found an app called DoubleFeel!\nIt helps us care for each other — track stress and sleep, view HRV and physical status, and stay updated on each other's health in real-time.\nCome join me 👉 https://doublefeel.cn/\nMy friend ID: {inviteCode}", 554 + "sharePartnerCodeTemplate": "Hey ❤️ I found an app called DoubleFeel!\nIt helps us care for each other — track stress and sleep, view HRV and physical status, and stay updated on each other's health in real-time.\nCome join me 👉 https://apps.apple.com/cn/app/doublefeel-%E5%8F%8C%E4%BA%BA%E6%83%85%E7%BB%AA%E5%85%B1%E4%BA%ABhrv%E5%8E%8B%E5%8A%9B%E6%B0%B4%E5%B9%B3%E8%87%AA%E6%B5%8B%E7%9D%A1%E7%9C%A0%E8%AE%B0%E5%BD%95/id6747254434\nMy friend ID: {inviteCode}",
555 "@sharePartnerCodeTemplate": { 555 "@sharePartnerCodeTemplate": {
556 "description": "Share partner code template", 556 "description": "Share partner code template",
557 "placeholders": { 557 "placeholders": {
@@ -693,4 +693,4 @@ @@ -693,4 +693,4 @@
693 "feedbackSubmitSuccessTitle": "Feedback submitted successfully", 693 "feedbackSubmitSuccessTitle": "Feedback submitted successfully",
694 "feedbackSubmitSuccessMessage": "Thank you for your feedback. If further communication is needed, we will contact you via the email address you left as soon as possible. Please keep an eye on your inbox.", 694 "feedbackSubmitSuccessMessage": "Thank you for your feedback. If further communication is needed, we will contact you via the email address you left as soon as possible. Please keep an eye on your inbox.",
695 "feedbackSubmitSuccessConfirm": "OK" 695 "feedbackSubmitSuccessConfirm": "OK"
696 -} 696 +}
@@ -930,7 +930,7 @@ @@ -930,7 +930,7 @@
930 "filming": "拍摄", 930 "filming": "拍摄",
931 "unlockTheProVersion": "解锁专业版", 931 "unlockTheProVersion": "解锁专业版",
932 "embarkOnAJourneyOfStressAwarenessAndWellnessSupport": "开启压力预警与健康陪伴之旅", 932 "embarkOnAJourneyOfStressAwarenessAndWellnessSupport": "开启压力预警与健康陪伴之旅",
933 - "sharePartnerCodeTemplate": "嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!\n它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。\n快来一起使用吧 👉 https://doublefeel.cn/\n我的好友id:{inviteCode}", 933 + "sharePartnerCodeTemplate": "嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!\n它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。\n快来一起使用吧 👉 https://apps.apple.com/cn/app/doublefeel-%E5%8F%8C%E4%BA%BA%E6%83%85%E7%BB%AA%E5%85%B1%E4%BA%ABhrv%E5%8E%8B%E5%8A%9B%E6%B0%B4%E5%B9%B3%E8%87%AA%E6%B5%8B%E7%9D%A1%E7%9C%A0%E8%AE%B0%E5%BD%95/id6747254434\n我的好友id:{inviteCode}",
934 "@sharePartnerCodeTemplate": { 934 "@sharePartnerCodeTemplate": {
935 "description": "分享好友邀请码文案模板", 935 "description": "分享好友邀请码文案模板",
936 "placeholders": { 936 "placeholders": {
@@ -3408,7 +3408,7 @@ abstract class AppLocalizations { @@ -3408,7 +3408,7 @@ abstract class AppLocalizations {
3408 /// 分享好友邀请码文案模板 3408 /// 分享好友邀请码文案模板
3409 /// 3409 ///
3410 /// In zh, this message translates to: 3410 /// In zh, this message translates to:
3411 - /// **'嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!\n它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。\n快来一起使用吧 👉 https://doublefeel.cn/\n我的好友id:{inviteCode}'** 3411 + /// **'嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!\n它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。\n快来一起使用吧 👉 https://apps.apple.com/cn/app/doublefeel-%E5%8F%8C%E4%BA%BA%E6%83%85%E7%BB%AA%E5%85%B1%E4%BA%ABhrv%E5%8E%8B%E5%8A%9B%E6%B0%B4%E5%B9%B3%E8%87%AA%E6%B5%8B%E7%9D%A1%E7%9C%A0%E8%AE%B0%E5%BD%95/id6747254434\n我的好友id:{inviteCode}'**
3412 String sharePartnerCodeTemplate(String inviteCode); 3412 String sharePartnerCodeTemplate(String inviteCode);
3413 3413
3414 /// No description provided for @bindPartnerIdNotExistTitle. 3414 /// No description provided for @bindPartnerIdNotExistTitle.
@@ -1914,7 +1914,7 @@ class AppLocalizationsEn extends AppLocalizations { @@ -1914,7 +1914,7 @@ class AppLocalizationsEn extends AppLocalizations {
1914 1914
1915 @override 1915 @override
1916 String sharePartnerCodeTemplate(String inviteCode) { 1916 String sharePartnerCodeTemplate(String inviteCode) {
1917 - return 'Hey ❤️ I found an app called DoubleFeel!\nIt helps us care for each other — track stress and sleep, view HRV and physical status, and stay updated on each other\'s health in real-time.\nCome join me 👉 https://doublefeel.cn/\nMy friend ID: $inviteCode'; 1917 + return 'Hey ❤️ I found an app called DoubleFeel!\nIt helps us care for each other — track stress and sleep, view HRV and physical status, and stay updated on each other\'s health in real-time.\nCome join me 👉 https://apps.apple.com/cn/app/doublefeel-%E5%8F%8C%E4%BA%BA%E6%83%85%E7%BB%AA%E5%85%B1%E4%BA%ABhrv%E5%8E%8B%E5%8A%9B%E6%B0%B4%E5%B9%B3%E8%87%AA%E6%B5%8B%E7%9D%A1%E7%9C%A0%E8%AE%B0%E5%BD%95/id6747254434\nMy friend ID: $inviteCode';
1918 } 1918 }
1919 1919
1920 @override 1920 @override
@@ -1816,7 +1816,7 @@ class AppLocalizationsZh extends AppLocalizations { @@ -1816,7 +1816,7 @@ class AppLocalizationsZh extends AppLocalizations {
1816 1816
1817 @override 1817 @override
1818 String sharePartnerCodeTemplate(String inviteCode) { 1818 String sharePartnerCodeTemplate(String inviteCode) {
1819 - return '嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!\n它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。\n快来一起使用吧 👉 https://doublefeel.cn/\n我的好友id:$inviteCode'; 1819 + return '嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!\n它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。\n快来一起使用吧 👉 https://apps.apple.com/cn/app/doublefeel-%E5%8F%8C%E4%BA%BA%E6%83%85%E7%BB%AA%E5%85%B1%E4%BA%ABhrv%E5%8E%8B%E5%8A%9B%E6%B0%B4%E5%B9%B3%E8%87%AA%E6%B5%8B%E7%9D%A1%E7%9C%A0%E8%AE%B0%E5%BD%95/id6747254434\n我的好友id:$inviteCode';
1820 } 1820 }
1821 1821
1822 @override 1822 @override