Commit 6057916c3a57140859e33e19eb909b78cd91e182

Authored by 常守达
1 parent 71db927b

fix(today):看板问题修复

@@ -8,6 +8,7 @@ import '../../core/services/user_state_service.dart'; @@ -8,6 +8,7 @@ import '../../core/services/user_state_service.dart';
8 import '../../data/local/local_storage.dart'; 8 import '../../data/local/local_storage.dart';
9 import '../../data/local/user_account_storage.dart'; 9 import '../../data/local/user_account_storage.dart';
10 import '../../data/local/user_preferences_storage.dart'; 10 import '../../data/local/user_preferences_storage.dart';
  11 +import '../../core/services/loading_service.dart';
11 import '../bindings/initial_binding.dart'; 12 import '../bindings/initial_binding.dart';
12 13
13 /// Application startup before [runApp]. 14 /// Application startup before [runApp].
@@ -36,6 +37,8 @@ abstract final class AppBootstrap { @@ -36,6 +37,8 @@ abstract final class AppBootstrap {
36 localStorage: local, 37 localStorage: local,
37 ).dependencies(); 38 ).dependencies();
38 39
  40 + LoadingService.init();
  41 +
39 if (local.termsAgreed) { 42 if (local.termsAgreed) {
40 await Get.find<UserStateService>().initializeSdks(); 43 await Get.find<UserStateService>().initializeSdks();
41 } 44 }
@@ -66,11 +66,8 @@ class BindPartnerController extends GetxController { @@ -66,11 +66,8 @@ class BindPartnerController extends GetxController {
66 66
67 void onShareMyCode() async { 67 void onShareMyCode() async {
68 try { 68 try {
69 - final success =  
70 - await PlatformHostApi().shareText("""嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!  
71 -它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。  
72 -快来一起使用吧 👉 https://doublefeel.cn/  
73 -我的好友id:${myInviteCode.value}"""); 69 + final success = await PlatformHostApi()
  70 + .shareText(l10n.sharePartnerCodeTemplate(myInviteCode.value));
74 } catch (e) { 71 } catch (e) {
75 AppToast.show(e.toString()); 72 AppToast.show(e.toString());
76 } 73 }
@@ -91,25 +88,25 @@ class BindPartnerController extends GetxController { @@ -91,25 +88,25 @@ class BindPartnerController extends GetxController {
91 switch (err.businessCode) { 88 switch (err.businessCode) {
92 case -1011201: 89 case -1011201:
93 DialogUtils.showCommonDialog(DialogMetaData( 90 DialogUtils.showCommonDialog(DialogMetaData(
94 - title: '该ID不存在',  
95 - message: '这个ID不存在哦,请检查后重新输入',  
96 - confirmText: '我知道了', 91 + title: l10n.bindPartnerIdNotExistTitle,
  92 + message: l10n.bindPartnerIdNotExistMessage,
  93 + confirmText: l10n.bindPartnerDialogGotIt,
97 iconAsset: null, 94 iconAsset: null,
98 )); 95 ));
99 break; 96 break;
100 case -1011202: 97 case -1011202:
101 DialogUtils.showCommonDialog(DialogMetaData( 98 DialogUtils.showCommonDialog(DialogMetaData(
102 - title: '添加失败',  
103 - message: '该用户不允许被添加好友,无法添加Ta哦',  
104 - confirmText: '我知道了', 99 + title: l10n.bindPartnerAddFailedTitle,
  100 + message: l10n.bindPartnerAddFailedMessage,
  101 + confirmText: l10n.bindPartnerDialogGotIt,
105 iconAsset: null, 102 iconAsset: null,
106 )); 103 ));
107 break; 104 break;
108 case -1011203: 105 case -1011203:
109 DialogUtils.showCommonDialog(DialogMetaData( 106 DialogUtils.showCommonDialog(DialogMetaData(
110 - title: '对方已经是你的好友啦',  
111 - message: '请不要重复添加哦',  
112 - confirmText: '我知道了', 107 + title: l10n.bindPartnerAlreadyFriendTitle,
  108 + message: l10n.bindPartnerAlreadyFriendMessage,
  109 + confirmText: l10n.bindPartnerDialogGotIt,
113 iconAsset: null, 110 iconAsset: null,
114 )); 111 ));
115 break; 112 break;
@@ -125,40 +122,6 @@ class BindPartnerController extends GetxController { @@ -125,40 +122,6 @@ class BindPartnerController extends GetxController {
125 } 122 }
126 break; 123 break;
127 } 124 }
128 - // if (result is AppFailure) {  
129 - // if (result.error is AppHttpError) {  
130 - // var error = result.error as AppHttpError;  
131 -  
132 - // switch (error.businessCode) {  
133 - // case -1011201:  
134 - // DialogUtils.showCommonDialog(DialogMetaData(  
135 - // title: '该ID不存在',  
136 - // message: '这个ID不存在哦,请检查后重新输入',  
137 - // confirmText: '我知道了',  
138 - // iconAsset: null,  
139 - // ));  
140 - // break;  
141 - // case -1011202:  
142 - // DialogUtils.showCommonDialog(DialogMetaData(  
143 - // title: '添加失败',  
144 - // message: '该用户不允许被添加好友,无法添加Ta哦',  
145 - // confirmText: '我知道了',  
146 - // iconAsset: null,  
147 - // ));  
148 - // break;  
149 - // case -1011203:  
150 - // DialogUtils.showCommonDialog(DialogMetaData(  
151 - // title: '对方已经是你的好友啦',  
152 - // message: '请不要重复添加哦',  
153 - // confirmText: '我知道了',  
154 - // iconAsset: null,  
155 - // ));  
156 - // break;  
157 - // }  
158 - // }  
159 - // return;  
160 - // }  
161 - // if (result is AppSuccess) {}  
162 } catch (e) { 125 } catch (e) {
163 } finally { 126 } finally {
164 isSubmitting.value = false; 127 isSubmitting.value = false;
@@ -168,25 +131,18 @@ class BindPartnerController extends GetxController { @@ -168,25 +131,18 @@ class BindPartnerController extends GetxController {
168 Future<void> onSkip() async { 131 Future<void> onSkip() async {
169 final userStateService = Get.find<UserStateService>(); 132 final userStateService = Get.find<UserStateService>();
170 if (!userStateService.isVip) { 133 if (!userStateService.isVip) {
171 - await Get.toNamed( 134 + // 使用 offNamed 移除 BIND_PARTNER,并将 onboarding 标识传入 PURCHASE
  135 + // 关闭 PURCHASE 后的跳转逻辑由 PurchaseController.onBackPressed() 接管
  136 + Get.offNamed(
172 Routes.PURCHASE, 137 Routes.PURCHASE,
173 - arguments: {PurchaseRouteArgs.showCloseButton: true}, 138 + arguments: {
  139 + PurchaseRouteArgs.showCloseButton: true,
  140 + 'from': 'onboarding',
  141 + },
174 ); 142 );
175 - final userStateService = Get.find<UserStateService>();  
176 - if (userStateService.isVip) {  
177 - Get.offAllNamed(AppRoutes.home);  
178 - } else {  
179 - await Future.delayed(Durations.medium1);  
180 - Get.toNamed(  
181 - Routes.MEMBERSHIP_OFFER,  
182 - arguments: {'from': 'onboarding'},  
183 - );  
184 - }  
185 } else { 143 } else {
186 Get.offAllNamed(AppRoutes.home); 144 Get.offAllNamed(AppRoutes.home);
187 } 145 }
188 - // Get.to(_BindSuccessPage(FriendItem(  
189 - // avatar: 'https://cdn.couple360.net/couple360/themes/im_theme7.png')));  
190 } 146 }
191 } 147 }
192 148
@@ -207,20 +163,13 @@ class _BindSuccessPage extends StatelessWidget { @@ -207,20 +163,13 @@ class _BindSuccessPage extends StatelessWidget {
207 onPressed: () async { 163 onPressed: () async {
208 final userStateService = Get.find<UserStateService>(); 164 final userStateService = Get.find<UserStateService>();
209 if (!userStateService.isVip) { 165 if (!userStateService.isVip) {
210 - await Get.toNamed( 166 + Get.offNamed(
211 Routes.PURCHASE, 167 Routes.PURCHASE,
212 - arguments: {PurchaseRouteArgs.showCloseButton: true}, 168 + arguments: {
  169 + PurchaseRouteArgs.showCloseButton: true,
  170 + 'from': 'onboarding',
  171 + },
213 ); 172 );
214 - final userStateService = Get.find<UserStateService>();  
215 - if (userStateService.isVip) {  
216 - Get.offAllNamed(AppRoutes.home);  
217 - } else {  
218 - await Future.delayed(Durations.medium1);  
219 - Get.toNamed(  
220 - Routes.MEMBERSHIP_OFFER,  
221 - arguments: {'from': 'onboarding'},  
222 - );  
223 - }  
224 } else { 173 } else {
225 Get.offAllNamed(AppRoutes.home); 174 Get.offAllNamed(AppRoutes.home);
226 } 175 }
@@ -187,9 +187,7 @@ class _FeedbackInput extends GetView<SubmitFeedbackController> { @@ -187,9 +187,7 @@ class _FeedbackInput extends GetView<SubmitFeedbackController> {
187 enabledBorder: InputBorder.none, 187 enabledBorder: InputBorder.none,
188 focusedBorder: InputBorder.none, 188 focusedBorder: InputBorder.none,
189 counterText: '', 189 counterText: '',
190 - hintText: '''1.请说明问题的界面和使用场景  
191 -2.请提供截图,以便提升解决问题效率  
192 -3.请留下联系方式,我们尽快回复联系您''', 190 + hintText: context.l10n.feedbackHintText,
193 hintStyle: TextStyle( 191 hintStyle: TextStyle(
194 color: context.colors.textTertiary, 192 color: context.colors.textTertiary,
195 fontSize: 14, 193 fontSize: 14,
@@ -21,6 +21,7 @@ import 'package:doublefeel_flutter/data/models/friend/friend_models.dart'; @@ -21,6 +21,7 @@ import 'package:doublefeel_flutter/data/models/friend/friend_models.dart';
21 import 'package:doublefeel_flutter/data/models/health/health_v2_models.dart'; 21 import 'package:doublefeel_flutter/data/models/health/health_v2_models.dart';
22 import 'package:doublefeel_flutter/data/models/local/user_preferences.dart'; 22 import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
23 import 'package:doublefeel_flutter/data/models/pay/pay_models.dart'; 23 import 'package:doublefeel_flutter/data/models/pay/pay_models.dart';
  24 +import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
24 import 'package:doublefeel_flutter/pigeon/health_kit_api.g.dart'; 25 import 'package:doublefeel_flutter/pigeon/health_kit_api.g.dart';
25 import 'package:doublefeel_flutter/pigeon/platform_api.g.dart'; 26 import 'package:doublefeel_flutter/pigeon/platform_api.g.dart';
26 import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart'; 27 import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart';
@@ -89,7 +90,7 @@ class TodayController extends GetMaterialController { @@ -89,7 +90,7 @@ class TodayController extends GetMaterialController {
89 90
90 final isLoadingToday = false.obs; 91 final isLoadingToday = false.obs;
91 final showHealthDataAuthCardStatus = 1.obs; 92 final showHealthDataAuthCardStatus = 1.obs;
92 - final stressSubtitle = 'Hi, 你今日的综合压力状态'.obs; 93 + final stressSubtitle = ''.obs;
93 94
94 // ── HRV 表盘引导 Banner ─────────────────── 95 // ── HRV 表盘引导 Banner ───────────────────
95 final showHrvAdBanner = false.obs; 96 final showHrvAdBanner = false.obs;
@@ -267,13 +268,13 @@ class TodayController extends GetMaterialController { @@ -267,13 +268,13 @@ class TodayController extends GetMaterialController {
267 if (showHealthDataAuthCardStatus.value == 1) { 268 if (showHealthDataAuthCardStatus.value == 1) {
268 return; 269 return;
269 } else if (showHealthDataAuthCardStatus.value == 0) { 270 } else if (showHealthDataAuthCardStatus.value == 0) {
270 - Get.to(NoHealthDataPage(  
271 - onRefresh: requestHealthAuthorization,  
272 - ));  
273 - } else {  
274 Get.to(NoHealthDataNoPermissionPage( 271 Get.to(NoHealthDataNoPermissionPage(
275 onRequest: requestHealthAuthorization, 272 onRequest: requestHealthAuthorization,
276 )); 273 ));
  274 + } else {
  275 + Get.to(NoHealthDataPage(
  276 + onRefresh: requestHealthAuthorization,
  277 + ));
277 } 278 }
278 } 279 }
279 280
@@ -296,18 +297,17 @@ class TodayController extends GetMaterialController { @@ -296,18 +297,17 @@ class TodayController extends GetMaterialController {
296 297
297 Future<void> checkHealthDataAuthCardVisible() async { 298 Future<void> checkHealthDataAuthCardVisible() async {
298 try { 299 try {
  300 + final result = await _hostApi.checkHealthAppAuthorization();
  301 +
  302 + if (result.status == 1) {
  303 + await _performDataUpload();
  304 + }
299 final hasUploaded = 305 final hasUploaded =
300 switch (await _healthApi.getHealthDataEverUploaded()) { 306 switch (await _healthApi.getHealthDataEverUploaded()) {
301 AppSuccess(:final data) => data, 307 AppSuccess(:final data) => data,
302 AppFailure() => false, 308 AppFailure() => false,
303 }; 309 };
304 - final result = await _hostApi.checkHealthAppAuthorization();  
305 -  
306 - if (result.status == 1) {  
307 - _performDataUpload();  
308 - }  
309 -  
310 - showHealthDataAuthCardStatus.value = !hasUploaded ? 0 : result.status; 310 + showHealthDataAuthCardStatus.value = !hasUploaded ? -1 : result.status;
311 } catch (e) {} 311 } catch (e) {}
312 } 312 }
313 313
@@ -316,7 +316,8 @@ class TodayController extends GetMaterialController { @@ -316,7 +316,8 @@ class TodayController extends GetMaterialController {
316 final intDate = int.parse(DateFormat('yyyyMMdd').format(date)); 316 final intDate = int.parse(DateFormat('yyyyMMdd').format(date));
317 317
318 final isToday = DateUtils.isSameDay(date, lastSelectableDay); 318 final isToday = DateUtils.isSameDay(date, lastSelectableDay);
319 - stressSubtitle.value = isToday ? 'Hi, 你今日的综合压力状态' : '该日压力状态'; 319 + stressSubtitle.value = (isFriend ? 'Ta' : '') +
  320 + (isToday ? l10n.overallStressLevelToday : l10n.stressLevelsOnThatDay);
320 switch (await _healthApi.getV2HealthData(friendUserId, intDate)) { 321 switch (await _healthApi.getV2HealthData(friendUserId, intDate)) {
321 case AppSuccess(:final data): 322 case AppSuccess(:final data):
322 v2HealthData.value = data; 323 v2HealthData.value = data;
@@ -271,7 +271,7 @@ class _Avatar extends StatelessWidget { @@ -271,7 +271,7 @@ class _Avatar extends StatelessWidget {
271 ), 271 ),
272 alignment: Alignment.center, 272 alignment: Alignment.center,
273 child: Text( 273 child: Text(
274 - '拍摄', 274 + l10n.filming,
275 style: TextStyle( 275 style: TextStyle(
276 color: context.colors.textPrimary, 276 color: context.colors.textPrimary,
277 fontSize: 14, 277 fontSize: 14,
@@ -298,7 +298,7 @@ class _Avatar extends StatelessWidget { @@ -298,7 +298,7 @@ class _Avatar extends StatelessWidget {
298 ), 298 ),
299 alignment: Alignment.center, 299 alignment: Alignment.center,
300 child: Text( 300 child: Text(
301 - '相册上传', 301 + l10n.uploadPhotos,
302 style: TextStyle( 302 style: TextStyle(
303 color: context.colors.textPrimary, 303 color: context.colors.textPrimary,
304 fontSize: 14, 304 fontSize: 14,
@@ -321,7 +321,7 @@ class _Avatar extends StatelessWidget { @@ -321,7 +321,7 @@ class _Avatar extends StatelessWidget {
321 ), 321 ),
322 alignment: Alignment.center, 322 alignment: Alignment.center,
323 child: Text( 323 child: Text(
324 - '取消', 324 + l10n.cancel,
325 style: TextStyle( 325 style: TextStyle(
326 color: context.colors.textSecondary, 326 color: context.colors.textSecondary,
327 fontSize: 14, 327 fontSize: 14,
@@ -396,8 +396,8 @@ class _UnlockPremiumCard extends StatelessWidget { @@ -396,8 +396,8 @@ class _UnlockPremiumCard extends StatelessWidget {
396 Color(0xFFFFFFFF) 396 Color(0xFFFFFFFF)
397 ], 397 ],
398 ).createShader(bounds), 398 ).createShader(bounds),
399 - child: const Text(  
400 - '解锁专业版', 399 + child: Text(
  400 + l10n.unlockTheProVersion,
401 style: TextStyle( 401 style: TextStyle(
402 color: Colors.white, 402 color: Colors.white,
403 fontSize: 16, 403 fontSize: 16,
@@ -409,14 +409,15 @@ class _UnlockPremiumCard extends StatelessWidget { @@ -409,14 +409,15 @@ class _UnlockPremiumCard extends StatelessWidget {
409 height: 4, 409 height: 4,
410 ), 410 ),
411 Text( 411 Text(
412 - '开启压力预警与健康陪伴之旅',  
413 - textAlign: TextAlign.center, 412 + l10n.embarkOnAJourneyOfStressAwarenessAndWellnessSupport,
  413 + textAlign: TextAlign.left,
414 style: TextStyle( 414 style: TextStyle(
415 - color: const Color(0xFFC6B2FF),  
416 - fontSize: 12,  
417 - fontFamily: 'PingFang SC',  
418 - fontWeight: FontWeight.w500,  
419 - ), 415 + color: const Color(0xFFC6B2FF),
  416 + fontSize: 12,
  417 + fontWeight: FontWeight.w500,
  418 + height: 1.2),
  419 + maxLines: 2,
  420 + overflow: TextOverflow.ellipsis,
420 ), 421 ),
421 Container( 422 Container(
422 height: 32, 423 height: 32,
@@ -445,7 +446,7 @@ class _UnlockPremiumCard extends StatelessWidget { @@ -445,7 +446,7 @@ class _UnlockPremiumCard extends StatelessWidget {
445 height: 18, 446 height: 18,
446 color: context.colors.textPrimary), 447 color: context.colors.textPrimary),
447 Text( 448 Text(
448 - '立即解锁', 449 + l10n.unlockNow,
449 style: TextStyle( 450 style: TextStyle(
450 color: context.colors.textPrimary, 451 color: context.colors.textPrimary,
451 fontSize: 14, 452 fontSize: 14,
@@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart'; @@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart';
2 2
3 import 'package:doublefeel_flutter/app/modules/home/widgets/today/hrv_measurement_bottom_sheet.dart'; 3 import 'package:doublefeel_flutter/app/modules/home/widgets/today/hrv_measurement_bottom_sheet.dart';
4 import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart'; 4 import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart';
  5 +import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_bottom_slogan.dart';
5 import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_date_picker_sheet.dart'; 6 import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_date_picker_sheet.dart';
6 7
7 import 'package:doublefeel_flutter/core/services/user_state_service.dart'; 8 import 'package:doublefeel_flutter/core/services/user_state_service.dart';
@@ -166,7 +167,7 @@ class TodayTabBody extends StatelessWidget { @@ -166,7 +167,7 @@ class TodayTabBody extends StatelessWidget {
166 mainAxisAlignment: MainAxisAlignment.center, 167 mainAxisAlignment: MainAxisAlignment.center,
167 children: [ 168 children: [
168 Text( 169 Text(
169 - controller.stressSubtitle.value, 170 + 'Hi,${controller.stressSubtitle.value}',
170 textAlign: TextAlign.center, 171 textAlign: TextAlign.center,
171 style: const TextStyle( 172 style: const TextStyle(
172 color: Color(0xFF78787D), 173 color: Color(0xFF78787D),
@@ -246,7 +247,10 @@ class TodayTabBody extends StatelessWidget { @@ -246,7 +247,10 @@ class TodayTabBody extends StatelessWidget {
246 Padding( 247 Padding(
247 padding: const EdgeInsets.symmetric(vertical: 12), 248 padding: const EdgeInsets.symmetric(vertical: 12),
248 child: Text( 249 child: Text(
249 - controller.isFriend ? 'TA的今日行动' : context.l10n.dailyActions, 250 + (controller.isFriend
  251 + ? context.l10n
  252 + .reportOtherPossessiveTitle(context.l10n.dailyActions)
  253 + : context.l10n.dailyActions),
250 style: TextStyle( 254 style: TextStyle(
251 color: context.colors.textPrimary, 255 color: context.colors.textPrimary,
252 fontSize: 16, 256 fontSize: 16,
@@ -259,14 +263,10 @@ class TodayTabBody extends StatelessWidget { @@ -259,14 +263,10 @@ class TodayTabBody extends StatelessWidget {
259 Container( 263 Container(
260 alignment: Alignment.center, 264 alignment: Alignment.center,
261 margin: EdgeInsets.only( 265 margin: EdgeInsets.only(
262 - bottom: 16 + MediaQuery.paddingOf(context).bottom,  
263 - top: 28,  
264 - ),  
265 - child: Image.asset(  
266 - 'assets/images/common/ic_bottom_slogan.png',  
267 - width: 223,  
268 - height: 35, 266 + bottom: 16,
  267 + // top: 28,
269 ), 268 ),
  269 + child: ReportBottomSlogan(),
270 ), 270 ),
271 ]), 271 ]),
272 ), 272 ),
@@ -425,7 +425,7 @@ class TodayTabBody extends StatelessWidget { @@ -425,7 +425,7 @@ class TodayTabBody extends StatelessWidget {
425 : null, 425 : null,
426 child: Text( 426 child: Text(
427 isSameDay(day, controller.lastSelectableDay) 427 isSameDay(day, controller.lastSelectableDay)
428 - ? AppLocalizations.of(context)!.today 428 + ? l10n.todayOnWeeklyCalendar
429 : _weekdayText(context, day), 429 : _weekdayText(context, day),
430 style: TextStyle( 430 style: TextStyle(
431 color: _calendarTextColor(context, day, selected: selected), 431 color: _calendarTextColor(context, day, selected: selected),
@@ -489,7 +489,7 @@ class TodayTabBody extends StatelessWidget { @@ -489,7 +489,7 @@ class TodayTabBody extends StatelessWidget {
489 return context.l10n.yesterday; 489 return context.l10n.yesterday;
490 } 490 }
491 if (isSameDay(selectedDay, today.subtract(const Duration(days: 2)))) { 491 if (isSameDay(selectedDay, today.subtract(const Duration(days: 2)))) {
492 - return '前天'; 492 + return l10n.theDayBeforeYesterday;
493 } 493 }
494 final locale = Localizations.localeOf(context).toString(); 494 final locale = Localizations.localeOf(context).toString();
495 if (selectedDay.year == today.year) { 495 if (selectedDay.year == today.year) {
@@ -571,7 +571,7 @@ class _LatestHrvCard extends StatelessWidget { @@ -571,7 +571,7 @@ class _LatestHrvCard extends StatelessWidget {
571 crossAxisAlignment: CrossAxisAlignment.start, 571 crossAxisAlignment: CrossAxisAlignment.start,
572 children: [ 572 children: [
573 Text( 573 Text(
574 - '最近一次$latestDataTimeText)', 574 + '${l10n.mostRecent}$latestDataTimeText)',
575 style: TextStyle( 575 style: TextStyle(
576 color: colors.textSecondary, 576 color: colors.textSecondary,
577 fontSize: 12, 577 fontSize: 12,
@@ -661,7 +661,8 @@ class _TopFriendBar extends StatelessWidget { @@ -661,7 +661,8 @@ class _TopFriendBar extends StatelessWidget {
661 ), 661 ),
662 ), 662 ),
663 Text( 663 Text(
664 - '${controller.targetFriendInfo.value?.remarkName}的状态', 664 + context.l10n.friendStatusTitle(
  665 + controller.targetFriendInfo.value?.remarkName ?? ''),
665 style: TextStyle( 666 style: TextStyle(
666 color: Colors.black, 667 color: Colors.black,
667 fontSize: 16, 668 fontSize: 16,
@@ -775,7 +776,7 @@ class _TopDateBar extends StatelessWidget { @@ -775,7 +776,7 @@ class _TopDateBar extends StatelessWidget {
775 ), 776 ),
776 const SizedBox(width: 2), 777 const SizedBox(width: 2),
777 Text( 778 Text(
778 - '20%优惠', 779 + l10n.purchaseAnnualDiscount,
779 style: TextStyle( 780 style: TextStyle(
780 fontSize: 14, 781 fontSize: 14,
781 fontWeight: FontWeight.w500, 782 fontWeight: FontWeight.w500,
@@ -6,6 +6,7 @@ import 'package:doublefeel_flutter/app/utils/dialog_utils.dart'; @@ -6,6 +6,7 @@ import 'package:doublefeel_flutter/app/utils/dialog_utils.dart';
6 6
7 import 'package:doublefeel_flutter/core/network/api/user_api.dart'; 7 import 'package:doublefeel_flutter/core/network/api/user_api.dart';
8 import 'package:doublefeel_flutter/core/result/app_result.dart'; 8 import 'package:doublefeel_flutter/core/result/app_result.dart';
  9 +import 'package:doublefeel_flutter/core/services/loading_service.dart';
9 import 'package:doublefeel_flutter/core/services/user_state_service.dart'; 10 import 'package:doublefeel_flutter/core/services/user_state_service.dart';
10 import 'package:doublefeel_flutter/core/theme/app_colors.dart'; 11 import 'package:doublefeel_flutter/core/theme/app_colors.dart';
11 import 'package:doublefeel_flutter/core/theme/app_theme.dart'; 12 import 'package:doublefeel_flutter/core/theme/app_theme.dart';
@@ -174,23 +175,28 @@ class AccountSettingView extends GetView<MyController> { @@ -174,23 +175,28 @@ class AccountSettingView extends GetView<MyController> {
174 175
175 Future<void> _logout() async { 176 Future<void> _logout() async {
176 final confirmResult = await DialogUtils.showCommonDialog(DialogMetaData( 177 final confirmResult = await DialogUtils.showCommonDialog(DialogMetaData(
177 - title: '退出登录',  
178 - message: '确认退出登录吗?',  
179 - confirmText: '确认退出', 178 + title: l10n.logOut,
  179 + message: l10n.confirmLogoutPrompt,
  180 + confirmText: l10n.confirmLogout,
180 iconAsset: null)); 181 iconAsset: null));
181 if (confirmResult.action != DialogAction.confirm) return; 182 if (confirmResult.action != DialogAction.confirm) return;
182 183
183 - await Get.find<UserStateService>().onLogout();  
184 - AppToast.show('已退出登录'); 184 + await LoadingService.instance.run(() async {
  185 + await Get.find<UserStateService>().onLogout();
  186 + });
  187 +
  188 + AppToast.show(l10n.loggedOutSuccessfully);
185 Get.offAllNamed(AppRoutes.login); 189 Get.offAllNamed(AppRoutes.login);
186 } 190 }
187 191
188 Future<void> _deleteAccount() async { 192 Future<void> _deleteAccount() async {
189 - final deleteResult = await Get.find<UserApi>().deleteAccount();  
190 - if (deleteResult is! AppSuccess<void>) return; 193 + await LoadingService.instance.run(() async {
  194 + final deleteResult = await Get.find<UserApi>().deleteAccount();
  195 + if (deleteResult is! AppSuccess<void>) return;
191 196
192 - await Get.find<UserStateService>().onLogout(callServerLogout: false);  
193 - AppToast.show('账号注销成功'); 197 + await Get.find<UserStateService>().onLogout(callServerLogout: false);
  198 + });
  199 + AppToast.show(l10n.accountDeletedSuccessfully);
194 Get.offAllNamed(AppRoutes.login); 200 Get.offAllNamed(AppRoutes.login);
195 } 201 }
196 } 202 }
@@ -269,7 +275,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget { @@ -269,7 +275,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget {
269 crossAxisAlignment: CrossAxisAlignment.start, 275 crossAxisAlignment: CrossAxisAlignment.start,
270 children: [ 276 children: [
271 Text( 277 Text(
272 - '注销账号后将无法找回!请谨慎操作', 278 + context.l10n.deleteAccountWarningTitle,
273 textAlign: TextAlign.center, 279 textAlign: TextAlign.center,
274 style: TextStyle( 280 style: TextStyle(
275 color: context.colors.warning, 281 color: context.colors.warning,
@@ -281,7 +287,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget { @@ -281,7 +287,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget {
281 height: 12, 287 height: 12,
282 ), 288 ),
283 Text( 289 Text(
284 - '提示:注销账号将会删除该账号内包括但不限于\n个人资料、情绪记录、统计数据等全部信息。', 290 + context.l10n.deleteAccountWarningPrompt,
285 style: TextStyle( 291 style: TextStyle(
286 color: context.colors.textSecondary, 292 color: context.colors.textSecondary,
287 fontSize: 12, 293 fontSize: 12,
@@ -292,7 +298,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget { @@ -292,7 +298,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget {
292 height: 12, 298 height: 12,
293 ), 299 ),
294 Text( 300 Text(
295 - '注1:你的健康数据会保存在苹果健康,我们不会删除苹果健康中的数据。', 301 + context.l10n.deleteAccountWarningNote1,
296 style: TextStyle( 302 style: TextStyle(
297 color: context.colors.textSecondary, 303 color: context.colors.textSecondary,
298 fontSize: 12, 304 fontSize: 12,
@@ -303,7 +309,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget { @@ -303,7 +309,7 @@ class _DeleteAccountBottomSheet extends StatelessWidget {
303 height: 12, 309 height: 12,
304 ), 310 ),
305 Text( 311 Text(
306 - '注2:删除账号不会影响你在App Store的订阅状态,如果需要取消订阅,请在APPs Store - 头像 - 订阅中手动取消订阅。', 312 + context.l10n.deleteAccountWarningNote2,
307 style: TextStyle( 313 style: TextStyle(
308 color: context.colors.textSecondary, 314 color: context.colors.textSecondary,
309 fontSize: 12, 315 fontSize: 12,
@@ -59,7 +59,7 @@ class PremiumCard extends StatelessWidget { @@ -59,7 +59,7 @@ class PremiumCard extends StatelessWidget {
59 crossAxisAlignment: CrossAxisAlignment.start, 59 crossAxisAlignment: CrossAxisAlignment.start,
60 children: [ 60 children: [
61 Text( 61 Text(
62 - '年度会员优惠', 62 + l10n.annualMemberDiscounts,
63 style: TextStyle( 63 style: TextStyle(
64 fontSize: 12, 64 fontSize: 12,
65 fontWeight: FontWeight.w500, 65 fontWeight: FontWeight.w500,
@@ -81,11 +81,11 @@ class PremiumCard extends StatelessWidget { @@ -81,11 +81,11 @@ class PremiumCard extends StatelessWidget {
81 ), 81 ),
82 ), 82 ),
83 const SizedBox(width: 4), 83 const SizedBox(width: 4),
84 - const Padding(  
85 - padding: EdgeInsets.only(bottom: 4), 84 + Padding(
  85 + padding: const EdgeInsets.only(bottom: 4),
86 child: Text( 86 child: Text(
87 - '优惠',  
88 - style: TextStyle( 87 + l10n.specialOffers,
  88 + style: const TextStyle(
89 fontSize: 16, 89 fontSize: 16,
90 fontWeight: FontWeight.w600, 90 fontWeight: FontWeight.w600,
91 color: _accent, 91 color: _accent,
@@ -102,7 +102,7 @@ class PremiumCard extends StatelessWidget { @@ -102,7 +102,7 @@ class PremiumCard extends StatelessWidget {
102 ? Row( 102 ? Row(
103 children: [ 103 children: [
104 Text( 104 Text(
105 - '现价', 105 + l10n.currentPrice,
106 style: TextStyle( 106 style: TextStyle(
107 fontSize: 12, 107 fontSize: 12,
108 fontWeight: FontWeight.w500, 108 fontWeight: FontWeight.w500,
@@ -120,7 +120,8 @@ class PremiumCard extends StatelessWidget { @@ -120,7 +120,8 @@ class PremiumCard extends StatelessWidget {
120 ), 120 ),
121 const SizedBox(width: 8), 121 const SizedBox(width: 8),
122 Text( 122 Text(
123 - '原价${controller.originDisplayPrice()}', 123 + l10n.originalPrice(
  124 + controller.originDisplayPrice()),
124 style: TextStyle( 125 style: TextStyle(
125 fontSize: 12, 126 fontSize: 12,
126 color: context.colors.textTertiary, 127 color: context.colors.textTertiary,
@@ -130,16 +130,21 @@ class TodayHrvChartCard extends StatelessWidget { @@ -130,16 +130,21 @@ class TodayHrvChartCard extends StatelessWidget {
130 130
131 return Row( 131 return Row(
132 children: [ 132 children: [
133 - Text(  
134 - controller.isFriend  
135 - ? (sameDay ? 'Ta的今日HRV趋势' : 'Ta的该日HRV趋势')  
136 - : (sameDay ? '今日HRV趋势' : context.l10n.averageHrvForTheDay),  
137 - style: TextStyle(  
138 - color: context.colors.textPrimary,  
139 - fontSize: 16,  
140 - fontWeight: FontWeight.w600,  
141 - ),  
142 - ), 133 + Builder(builder: (context) {
  134 + final s = (sameDay
  135 + ? context.l10n.todaySHrvTrend
  136 + : context.l10n.averageHrvForTheDay);
  137 + return Text(
  138 + (controller.isFriend
  139 + ? context.l10n.reportOtherPossessiveTitle(s)
  140 + : s),
  141 + style: TextStyle(
  142 + color: context.colors.textPrimary,
  143 + fontSize: 16,
  144 + fontWeight: FontWeight.w600,
  145 + ),
  146 + );
  147 + }),
143 GestureDetector( 148 GestureDetector(
144 behavior: HitTestBehavior.opaque, 149 behavior: HitTestBehavior.opaque,
145 onTap: () => showHrvPrincipleExplanationBottomSheet(context), 150 onTap: () => showHrvPrincipleExplanationBottomSheet(context),
@@ -353,7 +358,7 @@ class TodayHrvChartCard extends StatelessWidget { @@ -353,7 +358,7 @@ class TodayHrvChartCard extends StatelessWidget {
353 return Stack( 358 return Stack(
354 children: [ 359 children: [
355 if (stressPoints.isEmpty) 360 if (stressPoints.isEmpty)
356 - _EmptyChart(text: '暂无压力数据') 361 + _EmptyChart(text: context.l10n.noPressureDataAvailableAtThisTime)
357 else 362 else
358 LayoutBuilder( 363 LayoutBuilder(
359 builder: (context, constraints) { 364 builder: (context, constraints) {
@@ -623,7 +628,7 @@ class TodayHrvChartCard extends StatelessWidget { @@ -623,7 +628,7 @@ class TodayHrvChartCard extends StatelessWidget {
623 color: context.colors.textTertiary, 628 color: context.colors.textTertiary,
624 ), 629 ),
625 Text( 630 Text(
626 - '会员可查看完整数据', 631 + context.l10n.membersCanViewTheCompleteData,
627 style: TextStyle( 632 style: TextStyle(
628 color: context.colors.textTertiary, 633 color: context.colors.textTertiary,
629 fontSize: 14, 634 fontSize: 14,
@@ -647,8 +652,8 @@ class TodayHrvChartCard extends StatelessWidget { @@ -647,8 +652,8 @@ class TodayHrvChartCard extends StatelessWidget {
647 borderRadius: BorderRadius.circular(24), 652 borderRadius: BorderRadius.circular(24),
648 ), 653 ),
649 ), 654 ),
650 - child: const Text(  
651 - '立即解锁', 655 + child: Text(
  656 + context.l10n.unlockNow,
652 textAlign: TextAlign.center, 657 textAlign: TextAlign.center,
653 style: TextStyle( 658 style: TextStyle(
654 color: Color(0xFF0F0F11), 659 color: Color(0xFF0F0F11),
@@ -695,15 +700,15 @@ class TodayHrvChartCard extends StatelessWidget { @@ -695,15 +700,15 @@ class TodayHrvChartCard extends StatelessWidget {
695 String _getStatusByState(int? state) { 700 String _getStatusByState(int? state) {
696 switch (state) { 701 switch (state) {
697 case 1: 702 case 1:
698 - return '压力过载'; 703 + return l10n.pressureOverload;
699 case 2: 704 case 2:
700 - return '注意压力'; 705 + return l10n.beMindfulOfStress;
701 case 3: 706 case 3:
702 - return '状态正常'; 707 + return l10n.statusNormal;
703 case 4: 708 case 4:
704 - return '状态优秀'; 709 + return l10n.inExcellentCondition;
705 } 710 }
706 - return '等待数据'; 711 + return l10n.waitingForData;
707 } 712 }
708 713
709 List<TextSpan>? _getTooltip(BarChartRodData rod, int x, int? state) { 714 List<TextSpan>? _getTooltip(BarChartRodData rod, int x, int? state) {
@@ -723,7 +728,8 @@ class TodayHrvChartCard extends StatelessWidget { @@ -723,7 +728,8 @@ class TodayHrvChartCard extends StatelessWidget {
723 ), 728 ),
724 ), 729 ),
725 TextSpan( 730 TextSpan(
726 - text: '♥ 压力 ${_formatNumber(rod.toY)} · ${_formatHour(x.toDouble())}', 731 + text:
  732 + '♥ ${l10n.pressure} ${_formatNumber(rod.toY)} · ${_formatHour(x.toDouble())}',
727 style: const TextStyle( 733 style: const TextStyle(
728 color: Color(0xFF78787D), 734 color: Color(0xFF78787D),
729 fontWeight: FontWeight.bold, 735 fontWeight: FontWeight.bold,
@@ -23,14 +23,17 @@ class TodayHrvNumberCard extends StatelessWidget { @@ -23,14 +23,17 @@ class TodayHrvNumberCard extends StatelessWidget {
23 var sameDay = DateUtils.isSameDay( 23 var sameDay = DateUtils.isSameDay(
24 controller.selectedDate.value, controller.lastSelectableDay); 24 controller.selectedDate.value, controller.lastSelectableDay);
25 25
  26 + var otherHrv = (sameDay
  27 + ? context.l10n.todaySHrvTrend
  28 + : context.l10n.averageHrvForTheDay);
26 return Row( 29 return Row(
27 children: [ 30 children: [
28 // HRV 31 // HRV
29 Expanded( 32 Expanded(
30 child: _NumberItem( 33 child: _NumberItem(
31 label: controller.isFriend 34 label: controller.isFriend
32 - ? (sameDay ? 'Ta的今日平均HRV' : 'Ta的该日平均HRV')  
33 - : (sameDay ? '今日平均HRV' : context.l10n.averageHrvForTheDay), 35 + ? context.l10n.reportOtherPossessiveTitle(otherHrv)
  36 + : otherHrv,
34 value: 37 value:
35 controller.v2HealthData.value?.hrvAvg?.toString() ?? '--', 38 controller.v2HealthData.value?.hrvAvg?.toString() ?? '--',
36 unit: 'ms', 39 unit: 'ms',
@@ -40,7 +43,8 @@ class TodayHrvNumberCard extends StatelessWidget { @@ -40,7 +43,8 @@ class TodayHrvNumberCard extends StatelessWidget {
40 Expanded( 43 Expanded(
41 child: _NumberItem( 44 child: _NumberItem(
42 label: controller.isFriend 45 label: controller.isFriend
43 - ? 'Ta的静息心率' 46 + ? context.l10n.reportOtherPossessiveTitle(
  47 + context.l10n.restingHeartRate)
44 : context.l10n.restingHeartRate, 48 : context.l10n.restingHeartRate,
45 value: controller.v2HealthData.value?.lastRestingHrValue 49 value: controller.v2HealthData.value?.lastRestingHrValue
46 ?.toString() ?? 50 ?.toString() ??
@@ -20,7 +20,7 @@ class TodaySleepCard extends StatelessWidget { @@ -20,7 +20,7 @@ class TodaySleepCard extends StatelessWidget {
20 20
21 int sleepDuration = healthData?.sleepDuration ?? 0; 21 int sleepDuration = healthData?.sleepDuration ?? 0;
22 int sleepHours = sleepDuration ~/ 3600; 22 int sleepHours = sleepDuration ~/ 3600;
23 - int sleepMinutes = sleepDuration % 60; 23 + int sleepMinutes = (sleepDuration % 3600) ~/ 60;
24 Color sleepStateColor = sleepDuration == 0 24 Color sleepStateColor = sleepDuration == 0
25 ? context.colors.textPrimary 25 ? context.colors.textPrimary
26 : healthData?.sleepStateColor(context); 26 : healthData?.sleepStateColor(context);
@@ -46,9 +46,10 @@ class TodaySleepCard extends StatelessWidget { @@ -46,9 +46,10 @@ class TodaySleepCard extends StatelessWidget {
46 label: context.l10n.duration, 46 label: context.l10n.duration,
47 labelColor: context.colors.primary, 47 labelColor: context.colors.primary,
48 valueSpans: [ 48 valueSpans: [
49 - _MetricValueSpan(sleepHours > 0 ? '$sleepHours' : '--', '小时'),  
50 _MetricValueSpan( 49 _MetricValueSpan(
51 - sleepMinutes > 0 ? '$sleepMinutes' : '--', '分钟'), 50 + sleepHours > 0 ? '$sleepHours' : '--', l10n.reportUnitHour),
  51 + _MetricValueSpan(sleepMinutes > 0 ? '$sleepMinutes' : '--',
  52 + l10n.reportUnitMinute),
52 ], 53 ],
53 ), 54 ),
54 _MetricData( 55 _MetricData(
@@ -121,22 +122,23 @@ class TodayActivityCard extends StatelessWidget { @@ -121,22 +122,23 @@ class TodayActivityCard extends StatelessWidget {
121 final exHours = exercise ~/ 3600; 122 final exHours = exercise ~/ 3600;
122 final exMinutes = (exercise % 3600) ~/ 60; 123 final exMinutes = (exercise % 3600) ~/ 60;
123 exerciseSpans = [ 124 exerciseSpans = [
124 - if (exHours > 0) _MetricValueSpan('$exHours', '小时'), 125 + if (exHours > 0) _MetricValueSpan('$exHours', l10n.reportUnitHour),
125 if (exMinutes > 0 || exHours == 0) 126 if (exMinutes > 0 || exHours == 0)
126 - _MetricValueSpan(exMinutes > 0 ? '$exMinutes' : '0', '分钟'), 127 + _MetricValueSpan(
  128 + exMinutes > 0 ? '$exMinutes' : '0', l10n.reportUnitMinute),
127 ]; 129 ];
128 } else { 130 } else {
129 - exerciseSpans = [_MetricValueSpan('--', '分钟')]; 131 + exerciseSpans = [_MetricValueSpan('--', l10n.reportUnitMinute)];
130 } 132 }
131 133
132 // stand 单位为秒,转换为小时+分钟 134 // stand 单位为秒,转换为小时+分钟
133 List<_MetricValueSpan> standSpans; 135 List<_MetricValueSpan> standSpans;
134 if (stand > 0 && (activityTarget?.stand ?? 0) > 0) { 136 if (stand > 0 && (activityTarget?.stand ?? 0) > 0) {
135 standSpans = [ 137 standSpans = [
136 - _MetricValueSpan('$stand', '小时'), 138 + _MetricValueSpan('$stand', l10n.reportUnitHour),
137 ]; 139 ];
138 } else { 140 } else {
139 - standSpans = [_MetricValueSpan('--', '小时')]; 141 + standSpans = [_MetricValueSpan('--', l10n.reportUnitHour)];
140 } 142 }
141 double activityProgress = 143 double activityProgress =
142 (activity > 0 && (activityTarget?.move ?? 0) > 0) 144 (activity > 0 && (activityTarget?.move ?? 0) > 0)
@@ -160,7 +162,7 @@ class TodayActivityCard extends StatelessWidget { @@ -160,7 +162,7 @@ class TodayActivityCard extends StatelessWidget {
160 label: context.l10n.event, 162 label: context.l10n.event,
161 labelColor: context.colors.warning, 163 labelColor: context.colors.warning,
162 valueSpans: [ 164 valueSpans: [
163 - _MetricValueSpan(activityStr, '千卡'), 165 + _MetricValueSpan(activityStr, l10n.reportUnitKcal),
164 ], 166 ],
165 ), 167 ),
166 _MetricData( 168 _MetricData(
@@ -19,6 +19,7 @@ import 'package:doublefeel_flutter/data/models/user/user_models.dart'; @@ -19,6 +19,7 @@ import 'package:doublefeel_flutter/data/models/user/user_models.dart';
19 import 'package:doublefeel_flutter/data/models/vip/vip_info.dart'; 19 import 'package:doublefeel_flutter/data/models/vip/vip_info.dart';
20 import 'package:doublefeel_flutter/core/result/app_result.dart'; 20 import 'package:doublefeel_flutter/core/result/app_result.dart';
21 import 'package:doublefeel_flutter/app/routes/app_pages.dart'; 21 import 'package:doublefeel_flutter/app/routes/app_pages.dart';
  22 +import 'package:doublefeel_flutter/core/services/loading_service.dart';
22 23
23 class LoginController extends GetxController { 24 class LoginController extends GetxController {
24 final UserApi _userApi = Get.find<UserApi>(); 25 final UserApi _userApi = Get.find<UserApi>();
@@ -30,6 +31,7 @@ class LoginController extends GetxController { @@ -30,6 +31,7 @@ class LoginController extends GetxController {
30 31
31 final phoneController = TextEditingController(); 32 final phoneController = TextEditingController();
32 final codeController = TextEditingController(); 33 final codeController = TextEditingController();
  34 + final codeFocusNode = FocusNode();
33 35
34 final phoneInput = ''.obs; 36 final phoneInput = ''.obs;
35 final codeInput = ''.obs; 37 final codeInput = ''.obs;
@@ -113,6 +115,7 @@ class LoginController extends GetxController { @@ -113,6 +115,7 @@ class LoginController extends GetxController {
113 _countdownTimer?.cancel(); 115 _countdownTimer?.cancel();
114 phoneController.dispose(); 116 phoneController.dispose();
115 codeController.dispose(); 117 codeController.dispose();
  118 + codeFocusNode.dispose();
116 super.onClose(); 119 super.onClose();
117 } 120 }
118 121
@@ -145,6 +148,7 @@ class LoginController extends GetxController { @@ -145,6 +148,7 @@ class LoginController extends GetxController {
145 AppToast.show( 148 AppToast.show(
146 AppLocalizations.of(Get.context!)!.phoneLoginCodeSentSuccess); 149 AppLocalizations.of(Get.context!)!.phoneLoginCodeSentSuccess);
147 _startCountdown(); 150 _startCountdown();
  151 + codeFocusNode.requestFocus();
148 } 152 }
149 } 153 }
150 154
@@ -175,27 +179,35 @@ class LoginController extends GetxController { @@ -175,27 +179,35 @@ class LoginController extends GetxController {
175 } 179 }
176 180
177 isLoggingIn.value = true; 181 isLoggingIn.value = true;
178 - final loginRes = await _userApi.login(  
179 - telephone: cleanPhone,  
180 - verifyCode: codeInput.value,  
181 - ); 182 + try {
  183 + await LoadingService.instance.run(() async {
  184 + final loginRes = await _userApi.login(
  185 + telephone: cleanPhone,
  186 + verifyCode: codeInput.value,
  187 + );
182 188
183 - if (loginRes is AppSuccess<LoginResponse>) {  
184 - await _handleLoginSuccess(loginRes.data);  
185 - await Get.find<LocalStorage>().setLastLoginMethod('phone');  
186 - } else { 189 + if (loginRes is AppSuccess<LoginResponse>) {
  190 + await _handleLoginSuccess(loginRes.data);
  191 + await Get.find<LocalStorage>().setLastLoginMethod('phone');
  192 + }
  193 + });
  194 + } finally {
187 isLoggingIn.value = false; 195 isLoggingIn.value = false;
188 } 196 }
189 } 197 }
190 198
191 Future<void> _loginWithApple(AppleSignInModel appleModel) async { 199 Future<void> _loginWithApple(AppleSignInModel appleModel) async {
192 isLoggingIn.value = true; 200 isLoggingIn.value = true;
193 - final loginRes = await _userApi.loginWithApple(appleModel); 201 + try {
  202 + await LoadingService.instance.run(() async {
  203 + final loginRes = await _userApi.loginWithApple(appleModel);
194 204
195 - if (loginRes is AppSuccess<LoginResponse>) {  
196 - await _handleLoginSuccess(loginRes.data, appleModel: appleModel);  
197 - await Get.find<LocalStorage>().setLastLoginMethod('apple');  
198 - } else { 205 + if (loginRes is AppSuccess<LoginResponse>) {
  206 + await _handleLoginSuccess(loginRes.data, appleModel: appleModel);
  207 + await Get.find<LocalStorage>().setLastLoginMethod('apple');
  208 + }
  209 + });
  210 + } finally {
199 isLoggingIn.value = false; 211 isLoggingIn.value = false;
200 } 212 }
201 } 213 }
@@ -284,8 +296,6 @@ class LoginController extends GetxController { @@ -284,8 +296,6 @@ class LoginController extends GetxController {
284 // 用户登录成功即代表同意了协议,持久化到本地供冷启动时 SDK 初始化判断使用 296 // 用户登录成功即代表同意了协议,持久化到本地供冷启动时 SDK 初始化判断使用
285 await Get.find<LocalStorage>().setTermsAgreed(true); 297 await Get.find<LocalStorage>().setTermsAgreed(true);
286 298
287 - isLoggingIn.value = false;  
288 -  
289 // 根据引导完成状态决定跳转目标 299 // 根据引导完成状态决定跳转目标
290 final userId = me.id ?? 0; 300 final userId = me.id ?? 0;
291 if (_userAccount.hasCompletedOnboarding(userId) || 301 if (_userAccount.hasCompletedOnboarding(userId) ||
@@ -301,7 +311,7 @@ class LoginController extends GetxController { @@ -301,7 +311,7 @@ class LoginController extends GetxController {
301 ); 311 );
302 } 312 }
303 } else { 313 } else {
304 - isLoggingIn.value = false; 314 + // userInfoRes 失败,外层 finally 会重置 isLoggingIn
305 } 315 }
306 } 316 }
307 } 317 }
@@ -171,6 +171,7 @@ class PhoneLoginView extends GetView<LoginController> { @@ -171,6 +171,7 @@ class PhoneLoginView extends GetView<LoginController> {
171 171
172 return TextField( 172 return TextField(
173 controller: controller.codeController, 173 controller: controller.codeController,
  174 + focusNode: controller.codeFocusNode,
174 keyboardType: TextInputType.text, 175 keyboardType: TextInputType.text,
175 maxLength: 8, 176 maxLength: 8,
176 style: TextStyle( 177 style: TextStyle(
@@ -17,126 +17,135 @@ class MembershipOfferView extends GetView<MembershipOfferController> { @@ -17,126 +17,135 @@ class MembershipOfferView extends GetView<MembershipOfferController> {
17 @override 17 @override
18 Widget build(BuildContext context) { 18 Widget build(BuildContext context) {
19 final l10n = context.l10n; 19 final l10n = context.l10n;
20 - return GuideCommonScaffold(  
21 - showGradientBg: true,  
22 - onBackPressed: () {  
23 - controller.onBackPressed();  
24 - },  
25 - backIcon: 'assets/images/common/ic_close.png',  
26 - bottom: OnboardingBottomButton(  
27 - label: controller.isFromOnboard ? l10n.continueButton : '免费兑换优惠',  
28 - enabled: true,  
29 - onPressed: () {  
30 - controller.unlock(); 20 + return PopScope(
  21 + canPop: !controller.isFromOnboard,
  22 + onPopInvokedWithResult: (didPop, _) {
  23 + if (!didPop) {
  24 + controller.onBackPressed();
  25 + }
31 }, 26 },
32 - ),  
33 - child: SingleChildScrollView(  
34 - physics: const ClampingScrollPhysics(),  
35 - padding: EdgeInsets.fromLTRB(  
36 - 0,  
37 - MediaQuery.paddingOf(context).top + 4,  
38 - 0,  
39 - 140,  
40 - ),  
41 - child: Stack(  
42 - children: [  
43 - IgnorePointer(  
44 - child: Lottie.asset('assets/lottie/scatter_flowers.json'), 27 + child: GuideCommonScaffold(
  28 + showGradientBg: true,
  29 + onBackPressed: () {
  30 + controller.onBackPressed();
  31 + },
  32 + backIcon: 'assets/images/common/ic_close.png',
  33 + bottom: OnboardingBottomButton(
  34 + label: controller.isFromOnboard
  35 + ? l10n.continueButton
  36 + : l10n.freeRedemptionOffer,
  37 + enabled: true,
  38 + onPressed: () {
  39 + controller.unlock();
  40 + },
  41 + ),
  42 + child: SingleChildScrollView(
  43 + physics: const ClampingScrollPhysics(),
  44 + padding: EdgeInsets.fromLTRB(
  45 + 0,
  46 + MediaQuery.paddingOf(context).top + 4,
  47 + 0,
  48 + 140,
45 ), 49 ),
46 - Column(  
47 - mainAxisSize: MainAxisSize.max, 50 + child: Stack(
48 children: [ 51 children: [
49 - Padding(  
50 - padding: const EdgeInsets.symmetric(horizontal: 16),  
51 - child: Column(  
52 - children: [  
53 - SizedBox(height: 48),  
54 - OnboardingTitleText(l10n.onboardingMemberTitle),  
55 - const SizedBox(height: 12),  
56 - OnboardingBodyText(  
57 - l10n.onboardingMemberBody,  
58 - fontSize: 14,  
59 - ),  
60 - ],  
61 - ), 52 + IgnorePointer(
  53 + child: Lottie.asset('assets/lottie/scatter_flowers.json'),
62 ), 54 ),
63 - const SizedBox(height: 12),  
64 - Image.asset(  
65 - 'assets/images/user_onboarding/ic_membership_box.png'),  
66 - const SizedBox(height: 4),  
67 - Obx(() {  
68 - final yearInfo = controller.yearlyProductAppleInfo.value;  
69 - final yearProduct = controller.yearlyProduct.value;  
70 -  
71 - return yearInfo == null || yearProduct == null  
72 - ? const SizedBox(child: CircularProgressIndicator())  
73 - : Column(  
74 - children: [  
75 - Text(  
76 - '原价${_originDisplayPrice(yearInfo)}/年',  
77 - textAlign: TextAlign.center,  
78 - style: const TextStyle(  
79 - color: Color(0xFFB0B0B6),  
80 - fontSize: 16,  
81 - decoration: TextDecoration.lineThrough,  
82 - decorationColor: Color(0xFFB0B0B6),  
83 - letterSpacing: 0,  
84 - ),  
85 - ),  
86 - const SizedBox(height: 6),  
87 - Text(  
88 - _displayPrice(yearInfo),  
89 - textAlign: TextAlign.center,  
90 - style: TextStyle(  
91 - color: context.colors.primary,  
92 - fontSize: 28,  
93 - fontWeight: FontWeight.w600,  
94 - letterSpacing: 0,  
95 - ),  
96 - ),  
97 - const SizedBox(height: 6),  
98 - Text(  
99 - _planSubtitle(yearProduct, yearInfo),  
100 - textAlign: TextAlign.center,  
101 - style: const TextStyle(  
102 - color: Color(0xFFB0B0B6),  
103 - fontSize: 12,  
104 - letterSpacing: 0,  
105 - ),  
106 - ),  
107 - ],  
108 - );  
109 - }),  
110 - const SizedBox(height: 58),  
111 - GestureDetector(  
112 - behavior: HitTestBehavior.opaque,  
113 - onTap: () {  
114 - Get.offNamed(Routes.PURCHASE);  
115 - },  
116 - child: Padding(  
117 - padding: const EdgeInsets.symmetric(  
118 - vertical: 12,  
119 - horizontal: 24, 55 + Column(
  56 + mainAxisSize: MainAxisSize.max,
  57 + children: [
  58 + Padding(
  59 + padding: const EdgeInsets.symmetric(horizontal: 16),
  60 + child: Column(
  61 + children: [
  62 + SizedBox(height: 48),
  63 + OnboardingTitleText(l10n.onboardingMemberTitle),
  64 + const SizedBox(height: 12),
  65 + OnboardingBodyText(
  66 + l10n.onboardingMemberBody,
  67 + fontSize: 14,
  68 + ),
  69 + ],
  70 + ),
120 ), 71 ),
121 - child: Text(  
122 - l10n.onboardingMemberAllOptions,  
123 - textAlign: TextAlign.center,  
124 - style: TextStyle(  
125 - color: context.colors.textPrimary,  
126 - fontSize: 12,  
127 - decoration: TextDecoration.underline,  
128 - decorationColor: context.colors.textPrimary,  
129 - letterSpacing: 0, 72 + const SizedBox(height: 12),
  73 + Image.asset(
  74 + 'assets/images/user_onboarding/ic_membership_box.png'),
  75 + const SizedBox(height: 4),
  76 + Obx(() {
  77 + final yearInfo = controller.yearlyProductAppleInfo.value;
  78 + final yearProduct = controller.yearlyProduct.value;
  79 +
  80 + return yearInfo == null || yearProduct == null
  81 + ? const SizedBox(child: CircularProgressIndicator())
  82 + : Column(
  83 + children: [
  84 + Text(
  85 + '原价${_originDisplayPrice(yearInfo)}/年',
  86 + textAlign: TextAlign.center,
  87 + style: const TextStyle(
  88 + color: Color(0xFFB0B0B6),
  89 + fontSize: 16,
  90 + decoration: TextDecoration.lineThrough,
  91 + decorationColor: Color(0xFFB0B0B6),
  92 + letterSpacing: 0,
  93 + ),
  94 + ),
  95 + const SizedBox(height: 6),
  96 + Text(
  97 + _displayPrice(yearInfo),
  98 + textAlign: TextAlign.center,
  99 + style: TextStyle(
  100 + color: context.colors.primary,
  101 + fontSize: 28,
  102 + fontWeight: FontWeight.w600,
  103 + letterSpacing: 0,
  104 + ),
  105 + ),
  106 + const SizedBox(height: 6),
  107 + Text(
  108 + _planSubtitle(yearProduct, yearInfo),
  109 + textAlign: TextAlign.center,
  110 + style: const TextStyle(
  111 + color: Color(0xFFB0B0B6),
  112 + fontSize: 12,
  113 + letterSpacing: 0,
  114 + ),
  115 + ),
  116 + ],
  117 + );
  118 + }),
  119 + const SizedBox(height: 58),
  120 + GestureDetector(
  121 + behavior: HitTestBehavior.opaque,
  122 + onTap: () {
  123 + Get.offNamed(Routes.PURCHASE);
  124 + },
  125 + child: Padding(
  126 + padding: const EdgeInsets.symmetric(
  127 + vertical: 12,
  128 + horizontal: 24,
  129 + ),
  130 + child: Text(
  131 + l10n.onboardingMemberAllOptions,
  132 + textAlign: TextAlign.center,
  133 + style: TextStyle(
  134 + color: context.colors.textPrimary,
  135 + fontSize: 12,
  136 + decoration: TextDecoration.underline,
  137 + decorationColor: context.colors.textPrimary,
  138 + letterSpacing: 0,
  139 + ),
  140 + ),
130 ), 141 ),
131 ), 142 ),
132 - ), 143 + ],
133 ), 144 ),
134 ], 145 ],
135 ), 146 ),
136 - ],  
137 - ),  
138 - ),  
139 - ); 147 + ),
  148 + ));
140 } 149 }
141 150
142 String _originDisplayPrice(AppleProductInfo appleInfo) { 151 String _originDisplayPrice(AppleProductInfo appleInfo) {
@@ -53,9 +53,15 @@ class PurchaseController extends GetxController { @@ -53,9 +53,15 @@ class PurchaseController extends GetxController {
53 final isUnlocking = false.obs; 53 final isUnlocking = false.obs;
54 final isRestoring = false.obs; 54 final isRestoring = false.obs;
55 55
  56 + bool isFromOnboard = false;
  57 +
56 @override 58 @override
57 void onInit() { 59 void onInit() {
58 super.onInit(); 60 super.onInit();
  61 + final args = Get.arguments;
  62 + if (args is Map) {
  63 + isFromOnboard = args['from'] == 'onboarding';
  64 + }
59 scrollController.addListener(_handleScroll); 65 scrollController.addListener(_handleScroll);
60 unawaited(loadProductList()); 66 unawaited(loadProductList());
61 } 67 }
@@ -69,6 +75,17 @@ class PurchaseController extends GetxController { @@ -69,6 +75,17 @@ class PurchaseController extends GetxController {
69 super.onClose(); 75 super.onClose();
70 } 76 }
71 77
  78 + void onBackPressed() {
  79 + if (isFromOnboard) {
  80 + Get.offNamed(
  81 + Routes.MEMBERSHIP_OFFER,
  82 + arguments: {'from': 'onboarding'},
  83 + );
  84 + } else {
  85 + Get.back();
  86 + }
  87 + }
  88 +
72 void _handleScroll() { 89 void _handleScroll() {
73 final next = scrollController.hasClients && scrollController.offset > 250; 90 final next = scrollController.hasClients && scrollController.offset > 250;
74 if (next != isScrolled.value) { 91 if (next != isScrolled.value) {
@@ -223,7 +240,12 @@ class PurchaseController extends GetxController { @@ -223,7 +240,12 @@ class PurchaseController extends GetxController {
223 try { 240 try {
224 await _refreshVipInfo(); 241 await _refreshVipInfo();
225 } finally { 242 } finally {
226 - Get.offNamed(Routes.PREMIUM_ACTIVATED); 243 + if (isFromOnboard) {
  244 + // onboarding 流程购买成功,清空栈进入首页
  245 + Get.offAllNamed(AppRoutes.home);
  246 + } else {
  247 + Get.offNamed(Routes.PREMIUM_ACTIVATED);
  248 + }
227 } 249 }
228 } 250 }
229 251
@@ -17,16 +17,21 @@ class PurchaseView extends GetView<PurchaseController> { @@ -17,16 +17,21 @@ class PurchaseView extends GetView<PurchaseController> {
17 17
18 @override 18 @override
19 Widget build(BuildContext context) { 19 Widget build(BuildContext context) {
20 - return AnnotatedRegion<SystemUiOverlayStyle>(  
21 - value: const SystemUiOverlayStyle(  
22 - statusBarColor: Colors.transparent,  
23 - statusBarIconBrightness: Brightness.dark,  
24 - statusBarBrightness: Brightness.light,  
25 - systemNavigationBarColor: PurchaseColors.background,  
26 - systemNavigationBarIconBrightness: Brightness.dark,  
27 - ),  
28 - child: Scaffold(  
29 - backgroundColor: PurchaseColors.background, 20 + return PopScope(
  21 + canPop: !controller.isFromOnboard,
  22 + onPopInvokedWithResult: (didPop, _) {
  23 + if (!didPop) controller.onBackPressed();
  24 + },
  25 + child: AnnotatedRegion<SystemUiOverlayStyle>(
  26 + value: const SystemUiOverlayStyle(
  27 + statusBarColor: Colors.transparent,
  28 + statusBarIconBrightness: Brightness.dark,
  29 + statusBarBrightness: Brightness.light,
  30 + systemNavigationBarColor: PurchaseColors.background,
  31 + systemNavigationBarIconBrightness: Brightness.dark,
  32 + ),
  33 + child: Scaffold(
  34 + backgroundColor: PurchaseColors.background,
30 body: Stack( 35 body: Stack(
31 children: [ 36 children: [
32 Positioned.fill( 37 Positioned.fill(
@@ -85,7 +90,7 @@ class PurchaseView extends GetView<PurchaseController> { @@ -85,7 +90,7 @@ class PurchaseView extends GetView<PurchaseController> {
85 ], 90 ],
86 ), 91 ),
87 ), 92 ),
88 - ); 93 + ));
89 } 94 }
90 } 95 }
91 96
@@ -190,7 +195,7 @@ class _PurchaseNavigationBar extends StatelessWidget { @@ -190,7 +195,7 @@ class _PurchaseNavigationBar extends StatelessWidget {
190 children: [ 195 children: [
191 const SizedBox(width: 4), 196 const SizedBox(width: 4),
192 IconButton( 197 IconButton(
193 - onPressed: Get.back, 198 + onPressed: controller.onBackPressed,
194 icon: Image.asset( 199 icon: Image.asset(
195 showCloseButton 200 showCloseButton
196 ? R.assetsImagesCloseIcon 201 ? R.assetsImagesCloseIcon
@@ -274,12 +274,6 @@ class UserOnboardingController extends GetxController { @@ -274,12 +274,6 @@ class UserOnboardingController extends GetxController {
274 return; 274 return;
275 } 275 }
276 276
277 - if (status.isDenied && GetPlatform.isIOS) {  
278 - // iOS 上被拒绝后无法再弹窗,引导去设置  
279 - await openAppSettings();  
280 - return;  
281 - }  
282 -  
283 // 未决定 → 弹出系统授权弹窗 277 // 未决定 → 弹出系统授权弹窗
284 final result = await Permission.notification.request(); 278 final result = await Permission.notification.request();
285 AppLogger.d(result.isGranted 279 AppLogger.d(result.isGranted
@@ -50,7 +50,7 @@ List<Widget> buildUserOnboardingPages(BuildContext context, @@ -50,7 +50,7 @@ List<Widget> buildUserOnboardingPages(BuildContext context,
50 _SelectionPage( 50 _SelectionPage(
51 pageIndex: 2, 51 pageIndex: 2,
52 title: l10n.onboardingStressGoalQuestion, 52 title: l10n.onboardingStressGoalQuestion,
53 - multiple: false, 53 + multiple: true,
54 options: [ 54 options: [
55 OnboardingOptionData( 55 OnboardingOptionData(
56 label: l10n.onboardingStressGoalSource, 56 label: l10n.onboardingStressGoalSource,
@@ -353,7 +353,7 @@ class _HrvResearchPageState extends State<_HrvResearchPage> { @@ -353,7 +353,7 @@ class _HrvResearchPageState extends State<_HrvResearchPage> {
353 353
354 void _startAutoScroll() { 354 void _startAutoScroll() {
355 _autoScrollTimer?.cancel(); 355 _autoScrollTimer?.cancel();
356 - _autoScrollTimer = Timer.periodic(const Duration(seconds: 3), (_) { 356 + _autoScrollTimer = Timer.periodic(const Duration(seconds: 2), (_) {
357 if (!mounted || _userScrolling) return; 357 if (!mounted || _userScrolling) return;
358 if (_pageController.hasClients) { 358 if (_pageController.hasClients) {
359 final next = (_pageController.page?.round() ?? _initialPage) + 1; 359 final next = (_pageController.page?.round() ?? _initialPage) + 1;
  1 +import 'package:flutter/material.dart';
  2 +import 'package:get/get.dart';
  3 +import 'package:lottie/lottie.dart';
  4 +
  5 +class LoadingService {
  6 + OverlayEntry? _entry;
  7 + int _counter = 0;
  8 +
  9 + static void init() {
  10 + Get.put(LoadingService());
  11 + }
  12 +
  13 + static LoadingService get instance => Get.find();
  14 +
  15 + /// 包装一个异步操作,执行期间显示 Loading,完成后自动隐藏。
  16 + Future<T> run<T>(Future<T> Function() block) async {
  17 + show();
  18 + try {
  19 + return await block();
  20 + } finally {
  21 + hide();
  22 + }
  23 + }
  24 +
  25 + void show() {
  26 + _counter++;
  27 + if (_counter > 1) return;
  28 +
  29 + // 直接从根 Navigator 获取 Overlay,避免 context 查找失败
  30 + final overlay = Get.key.currentState?.overlay;
  31 + if (overlay == null) {
  32 + _counter--;
  33 + return;
  34 + }
  35 +
  36 + // 防御:清理可能残留的旧 entry
  37 + _entry?.remove();
  38 + _entry = OverlayEntry(builder: (_) => const _GlobalLoadingWidget());
  39 + overlay.insert(_entry!);
  40 + }
  41 +
  42 + void hide() {
  43 + if (_counter <= 0) return;
  44 + _counter--;
  45 + if (_counter == 0) {
  46 + _entry?.remove();
  47 + _entry = null;
  48 + }
  49 + }
  50 +}
  51 +
  52 +class _GlobalLoadingWidget extends StatelessWidget {
  53 + const _GlobalLoadingWidget();
  54 +
  55 + @override
  56 + Widget build(BuildContext context) {
  57 + // GestureDetector 吸收所有点击,防止穿透触发底层 UI
  58 + return GestureDetector(
  59 + behavior: HitTestBehavior.opaque,
  60 + onTap: () {},
  61 + child: SizedBox.expand(
  62 + child: ColoredBox(
  63 + color: Colors.black.withValues(alpha: 0.3),
  64 + child: Align(
  65 + alignment: const Alignment(0.0, -0.1),
  66 + child: Lottie.asset(
  67 + 'assets/lottie/loading.json',
  68 + width: 130,
  69 + height: 90,
  70 + repeat: true,
  71 + animate: true,
  72 + ),
  73 + ),
  74 + ),
  75 + ),
  76 + );
  77 + }
  78 +}
1 import 'dart:ui'; 1 import 'dart:ui';
2 2
3 import 'package:doublefeel_flutter/core/theme/app_theme.dart'; 3 import 'package:doublefeel_flutter/core/theme/app_theme.dart';
  4 +import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
4 import 'package:flutter/src/widgets/framework.dart'; 5 import 'package:flutter/src/widgets/framework.dart';
5 6
6 int? _parseInt(dynamic value) { 7 int? _parseInt(dynamic value) {
@@ -36,15 +37,15 @@ class V2LatestHrvData { @@ -36,15 +37,15 @@ class V2LatestHrvData {
36 String stateString() { 37 String stateString() {
37 switch (state) { 38 switch (state) {
38 case 1: 39 case 1:
39 - return '压力过载'; 40 + return l10n.pressureOverload;
40 case 2: 41 case 2:
41 - return '注意压力'; 42 + return l10n.beMindfulOfStress;
42 case 3: 43 case 3:
43 - return '状态正常'; 44 + return l10n.statusNormal;
44 case 4: 45 case 4:
45 - return '状态优秀'; 46 + return l10n.inExcellentCondition;
46 } 47 }
47 - return '等待数据'; 48 + return l10n.waitingForData;
48 } 49 }
49 50
50 stateColor(BuildContext context) { 51 stateColor(BuildContext context) {
@@ -187,11 +188,11 @@ class V2HealthData { @@ -187,11 +188,11 @@ class V2HealthData {
187 sleepStateStr() { 188 sleepStateStr() {
188 switch (sleepState) { 189 switch (sleepState) {
189 case 1: 190 case 1:
190 - return '优秀'; 191 + return l10n.sleepQualityExcellent;
191 case 2: 192 case 2:
192 - return '正常'; 193 + return l10n.sleepQualityNormal;
193 case 3: 194 case 3:
194 - return '注意'; 195 + return l10n.sleepQualityAttention;
195 } 196 }
196 return '--'; 197 return '--';
197 } 198 }
@@ -238,15 +239,15 @@ class V2StressScore { @@ -238,15 +239,15 @@ class V2StressScore {
238 String stateString() { 239 String stateString() {
239 switch (state) { 240 switch (state) {
240 case 1: 241 case 1:
241 - return '压力过载'; 242 + return l10n.pressureOverload;
242 case 2: 243 case 2:
243 - return '注意压力'; 244 + return l10n.beMindfulOfStress;
244 case 3: 245 case 3:
245 - return '状态正常'; 246 + return l10n.statusNormal;
246 case 4: 247 case 4:
247 - return '状态优秀'; 248 + return l10n.inExcellentCondition;
248 } 249 }
249 - return '等待数据'; 250 + return l10n.waitingForData;
250 } 251 }
251 252
252 String stateBg() { 253 String stateBg() {
@@ -250,6 +250,7 @@ @@ -250,6 +250,7 @@
250 "submit": "Submit", 250 "submit": "Submit",
251 "questionsAndFeedback": "Questions and Feedback", 251 "questionsAndFeedback": "Questions and Feedback",
252 "ifYouWouldLikeUsToReplyPleaseProvideYourEmailAddress": "If you would like us to reply, please provide your email address", 252 "ifYouWouldLikeUsToReplyPleaseProvideYourEmailAddress": "If you would like us to reply, please provide your email address",
  253 + "feedbackHintText": "1. Please describe the screen and scenario where the issue occurred\n2. Please provide screenshots to help us resolve the issue more efficiently\n3. Please leave your contact information so we can get back to you as soon as possible",
253 "uploadProof": "Upload Proof", 254 "uploadProof": "Upload Proof",
254 "frequentlyAskedQuestions": "Frequently Asked Questions", 255 "frequentlyAskedQuestions": "Frequently Asked Questions",
255 "areYouSureYouWantToDeleteYourAccount": "Are you sure you want to delete your account?", 256 "areYouSureYouWantToDeleteYourAccount": "Are you sure you want to delete your account?",
@@ -257,6 +258,14 @@ @@ -257,6 +258,14 @@
257 "deleteAccount": "Delete Account", 258 "deleteAccount": "Delete Account",
258 "mobilePhoneNumber": "Mobile phone number", 259 "mobilePhoneNumber": "Mobile phone number",
259 "logOut": "Log Out", 260 "logOut": "Log Out",
  261 + "confirmLogoutPrompt": "Are you sure you want to log out?",
  262 + "confirmLogout": "Log Out",
  263 + "loggedOutSuccessfully": "Logged out successfully",
  264 + "accountDeletedSuccessfully": "Account deleted successfully",
  265 + "deleteAccountWarningTitle": "Once deleted, the account cannot be recovered! Please proceed with caution.",
  266 + "deleteAccountWarningPrompt": "Note: Deleting the account will remove all information in this account, including but not limited to\npersonal profile, mood records, and statistical data.",
  267 + "deleteAccountWarningNote1": "Note 1: Your health data will be saved in Apple Health, and we will not delete data from Apple Health.",
  268 + "deleteAccountWarningNote2": "Note 2: Deleting the account will not affect your subscription status in the App Store. If you need to cancel the subscription, please cancel it manually in the App Store -> Profile -> Subscriptions.",
260 "confirmDeletion": "Confirm Deletion", 269 "confirmDeletion": "Confirm Deletion",
261 "iLlThinkAboutItSomeMore": "I'll think about it some more.", 270 "iLlThinkAboutItSomeMore": "I'll think about it some more.",
262 "sleep": "Sleep", 271 "sleep": "Sleep",
@@ -494,5 +503,62 @@ @@ -494,5 +503,62 @@
494 "appReviewFeedbackMessage": "Would you tell us what went wrong?\nYour feedback helps us improve the stress and health experience. 💜", 503 "appReviewFeedbackMessage": "Would you tell us what went wrong?\nYour feedback helps us improve the stress and health experience. 💜",
495 "appReviewFeedbackSendAction": "Send Feedback", 504 "appReviewFeedbackSendAction": "Send Feedback",
496 "appReviewFeedbackLaterAction": "Maybe Later", 505 "appReviewFeedbackLaterAction": "Maybe Later",
497 - "appReviewIllustrationPlaceholder": "Illustration Placeholder"  
498 -} 506 + "appReviewIllustrationPlaceholder": "Illustration Placeholder",
  507 + "overallStressLevelToday": "Overall Stress Level Today",
  508 + "stressLevelsOnThatDay": "Stress Levels on That Day",
  509 + "noPressureDataAvailableAtThisTime": "No pressure data available at this time",
  510 + "membersCanViewTheCompleteData": "Members can view the complete data",
  511 + "unlockNow": "Unlock Now",
  512 + "pressureOverload": "Pressure Overload",
  513 + "beMindfulOfStress": "Be Aware of Stress",
  514 + "statusNormal": "Status: Normal",
  515 + "inExcellentCondition": "In excellent condition",
  516 + "waitingForData": "Waiting for data",
  517 + "pressure": "Pressure",
  518 + "mostRecent": "Most recent",
  519 + "theDayBeforeYesterday": "The day before yesterday",
  520 + "uploadPhotos": "Upload Photos",
  521 + "filming": "Filming",
  522 + "unlockTheProVersion": "Unlock the Pro Version",
  523 + "embarkOnAJourneyOfStressAwarenessAndWellnessSupport": "Embark on a Journey of Stress Awareness and Wellness Support",
  524 + "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}",
  525 + "@sharePartnerCodeTemplate": {
  526 + "description": "Share partner code template",
  527 + "placeholders": {
  528 + "inviteCode": {
  529 + "type": "String"
  530 + }
  531 + }
  532 + },
  533 + "bindPartnerIdNotExistTitle": "ID does not exist",
  534 + "bindPartnerIdNotExistMessage": "This ID does not exist. Please check and try again",
  535 + "bindPartnerDialogGotIt": "Got it",
  536 + "bindPartnerAddFailedTitle": "Failed to add",
  537 + "bindPartnerAddFailedMessage": "This user does not allow adding friends, cannot add them",
  538 + "bindPartnerAlreadyFriendTitle": "They are already your friend",
  539 + "bindPartnerAlreadyFriendMessage": "Please do not add again",
  540 + "friendStatusTitle": "{remarkName}'s Status",
  541 + "@friendStatusTitle": {
  542 + "description": "Friend status title",
  543 + "placeholders": {
  544 + "remarkName": {
  545 + "type": "String"
  546 + }
  547 + }
  548 + },
  549 + "annualMemberDiscounts": "Annual Member Discounts",
  550 + "specialOffers": "Special Offers",
  551 + "currentPrice": "Current Price",
  552 + "originalPrice": "Original price {price}",
  553 + "@originalPrice": {
  554 + "description": "Original price label",
  555 + "placeholders": {
  556 + "price": {
  557 + "type": "String"
  558 + }
  559 + }
  560 + },
  561 + "freeRedemptionOffer": "Free Redemption Offer",
  562 + "cellPhoneNumber": "Cell phone number",
  563 + "todayOnWeeklyCalendar": "today"
  564 +}
@@ -333,6 +333,7 @@ @@ -333,6 +333,7 @@
333 "submit": "提交", 333 "submit": "提交",
334 "questionsAndFeedback": "问题和反馈", 334 "questionsAndFeedback": "问题和反馈",
335 "ifYouWouldLikeUsToReplyPleaseProvideYourEmailAddress": "如果需要我们回复,请填写联系邮箱", 335 "ifYouWouldLikeUsToReplyPleaseProvideYourEmailAddress": "如果需要我们回复,请填写联系邮箱",
  336 + "feedbackHintText": "1.请说明问题的界面和使用场景\n2.请提供截图,以便提升解决问题效率\n3.请留下联系方式,我们尽快回复联系您",
336 "uploadProof": "上传凭证", 337 "uploadProof": "上传凭证",
337 "frequentlyAskedQuestions": "常见问题", 338 "frequentlyAskedQuestions": "常见问题",
338 "areYouSureYouWantToDeleteYourAccount": "确认注销账号吗?", 339 "areYouSureYouWantToDeleteYourAccount": "确认注销账号吗?",
@@ -340,6 +341,14 @@ @@ -340,6 +341,14 @@
340 "deleteAccount": "注销账号", 341 "deleteAccount": "注销账号",
341 "mobilePhoneNumber": "手机号", 342 "mobilePhoneNumber": "手机号",
342 "logOut": "退出登录", 343 "logOut": "退出登录",
  344 + "confirmLogoutPrompt": "确认退出登录吗?",
  345 + "confirmLogout": "确认退出",
  346 + "loggedOutSuccessfully": "已退出登录",
  347 + "accountDeletedSuccessfully": "账号注销成功",
  348 + "deleteAccountWarningTitle": "注销账号后将无法找回!请谨慎操作",
  349 + "deleteAccountWarningPrompt": "提示:注销账号将会删除该账号内包括但不限于\n个人资料、情绪记录、统计数据等全部信息。",
  350 + "deleteAccountWarningNote1": "注1:你的健康数据会保存在苹果健康,我们不会删除苹果健康中的数据。",
  351 + "deleteAccountWarningNote2": "注2:删除账号不会影响你在App Store的订阅状态,如果需要取消订阅,请在APPs Store - 头像 - 订阅中手动取消订阅。",
343 "confirmDeletion": "确认注销", 352 "confirmDeletion": "确认注销",
344 "iLlThinkAboutItSomeMore": "我再想想", 353 "iLlThinkAboutItSomeMore": "我再想想",
345 "sleep": "睡眠", 354 "sleep": "睡眠",
@@ -741,6 +750,9 @@ @@ -741,6 +750,9 @@
741 "purchaseNoteContact": "如果有更多其他问题,请联系我们。", 750 "purchaseNoteContact": "如果有更多其他问题,请联系我们。",
742 "reportBottomSlogan": "双倍感知 压力减半", 751 "reportBottomSlogan": "双倍感知 压力减半",
743 "refundExplanationTitle": "退费说明", 752 "refundExplanationTitle": "退费说明",
  753 + "@refundExplanationTitle": {
  754 + "description": "退费说明"
  755 + },
744 "refundAppStoreReviewTitle": "退款由 App Store 审核处理", 756 "refundAppStoreReviewTitle": "退款由 App Store 审核处理",
745 "refundAppStoreReviewDescription": "所有订阅与虚拟商品均通过 App Store 官方支付系统完成,DoubleFeel 无法直接处理付款或退款。", 757 "refundAppStoreReviewDescription": "所有订阅与虚拟商品均通过 App Store 官方支付系统完成,DoubleFeel 无法直接处理付款或退款。",
746 "refundAppleRulesIntroduction": "根据 Apple 的平台规则:", 758 "refundAppleRulesIntroduction": "根据 Apple 的平台规则:",
@@ -779,5 +791,62 @@ @@ -779,5 +791,62 @@
779 "appReviewFeedbackMessage": "愿意告诉我们遇到了什么问题吗?\n你的反馈可以帮助我们持续改进压力与健康体验 💜", 791 "appReviewFeedbackMessage": "愿意告诉我们遇到了什么问题吗?\n你的反馈可以帮助我们持续改进压力与健康体验 💜",
780 "appReviewFeedbackSendAction": "发送反馈", 792 "appReviewFeedbackSendAction": "发送反馈",
781 "appReviewFeedbackLaterAction": "稍后再说", 793 "appReviewFeedbackLaterAction": "稍后再说",
782 - "appReviewIllustrationPlaceholder": "插图占位"  
783 -} 794 + "appReviewIllustrationPlaceholder": "插图占位",
  795 + "overallStressLevelToday": "今日的综合压力状态",
  796 + "stressLevelsOnThatDay": "该日压力状态",
  797 + "noPressureDataAvailableAtThisTime": "暂无压力数据",
  798 + "membersCanViewTheCompleteData": "会员可查看完整数据",
  799 + "unlockNow": "立即解锁",
  800 + "pressureOverload": "压力过载",
  801 + "beMindfulOfStress": "注意压力",
  802 + "statusNormal": "状态正常",
  803 + "inExcellentCondition": "状态优秀",
  804 + "waitingForData": "等待数据",
  805 + "pressure": "压力",
  806 + "mostRecent": "最近一次",
  807 + "theDayBeforeYesterday": "前天",
  808 + "uploadPhotos": "相册上传",
  809 + "filming": "拍摄",
  810 + "unlockTheProVersion": "解锁专业版",
  811 + "embarkOnAJourneyOfStressAwarenessAndWellnessSupport": "开启压力预警与健康陪伴之旅",
  812 + "sharePartnerCodeTemplate": "嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!\n它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。\n快来一起使用吧 👉 https://doublefeel.cn/\n我的好友id:{inviteCode}",
  813 + "@sharePartnerCodeTemplate": {
  814 + "description": "分享好友邀请码文案模板",
  815 + "placeholders": {
  816 + "inviteCode": {
  817 + "type": "String"
  818 + }
  819 + }
  820 + },
  821 + "bindPartnerIdNotExistTitle": "该ID不存在",
  822 + "bindPartnerIdNotExistMessage": "这个ID不存在哦,请检查后重新输入",
  823 + "bindPartnerDialogGotIt": "我知道了",
  824 + "bindPartnerAddFailedTitle": "添加失败",
  825 + "bindPartnerAddFailedMessage": "该用户不允许被添加好友,无法添加Ta哦",
  826 + "bindPartnerAlreadyFriendTitle": "对方已经是你的好友啦",
  827 + "bindPartnerAlreadyFriendMessage": "请不要重复添加哦",
  828 + "friendStatusTitle": "{remarkName}的状态",
  829 + "@friendStatusTitle": {
  830 + "description": "好友的状态标题",
  831 + "placeholders": {
  832 + "remarkName": {
  833 + "type": "String"
  834 + }
  835 + }
  836 + },
  837 + "annualMemberDiscounts": "年度会员优惠",
  838 + "specialOffers": "优惠",
  839 + "currentPrice": "现价",
  840 + "originalPrice": "原价{price}",
  841 + "@originalPrice": {
  842 + "description": "原价标签",
  843 + "placeholders": {
  844 + "price": {
  845 + "type": "String"
  846 + }
  847 + }
  848 + },
  849 + "freeRedemptionOffer": "免费兑换优惠",
  850 + "cellPhoneNumber": "手机号",
  851 + "todayOnWeeklyCalendar": "今"
  852 +}
@@ -1599,6 +1599,12 @@ abstract class AppLocalizations { @@ -1599,6 +1599,12 @@ abstract class AppLocalizations {
1599 /// **'如果需要我们回复,请填写联系邮箱'** 1599 /// **'如果需要我们回复,请填写联系邮箱'**
1600 String get ifYouWouldLikeUsToReplyPleaseProvideYourEmailAddress; 1600 String get ifYouWouldLikeUsToReplyPleaseProvideYourEmailAddress;
1601 1601
  1602 + /// No description provided for @feedbackHintText.
  1603 + ///
  1604 + /// In zh, this message translates to:
  1605 + /// **'1.请说明问题的界面和使用场景\n2.请提供截图,以便提升解决问题效率\n3.请留下联系方式,我们尽快回复联系您'**
  1606 + String get feedbackHintText;
  1607 +
1602 /// No description provided for @uploadProof. 1608 /// No description provided for @uploadProof.
1603 /// 1609 ///
1604 /// In zh, this message translates to: 1610 /// In zh, this message translates to:
@@ -1641,6 +1647,54 @@ abstract class AppLocalizations { @@ -1641,6 +1647,54 @@ abstract class AppLocalizations {
1641 /// **'退出登录'** 1647 /// **'退出登录'**
1642 String get logOut; 1648 String get logOut;
1643 1649
  1650 + /// No description provided for @confirmLogoutPrompt.
  1651 + ///
  1652 + /// In zh, this message translates to:
  1653 + /// **'确认退出登录吗?'**
  1654 + String get confirmLogoutPrompt;
  1655 +
  1656 + /// No description provided for @confirmLogout.
  1657 + ///
  1658 + /// In zh, this message translates to:
  1659 + /// **'确认退出'**
  1660 + String get confirmLogout;
  1661 +
  1662 + /// No description provided for @loggedOutSuccessfully.
  1663 + ///
  1664 + /// In zh, this message translates to:
  1665 + /// **'已退出登录'**
  1666 + String get loggedOutSuccessfully;
  1667 +
  1668 + /// No description provided for @accountDeletedSuccessfully.
  1669 + ///
  1670 + /// In zh, this message translates to:
  1671 + /// **'账号注销成功'**
  1672 + String get accountDeletedSuccessfully;
  1673 +
  1674 + /// No description provided for @deleteAccountWarningTitle.
  1675 + ///
  1676 + /// In zh, this message translates to:
  1677 + /// **'注销账号后将无法找回!请谨慎操作'**
  1678 + String get deleteAccountWarningTitle;
  1679 +
  1680 + /// No description provided for @deleteAccountWarningPrompt.
  1681 + ///
  1682 + /// In zh, this message translates to:
  1683 + /// **'提示:注销账号将会删除该账号内包括但不限于\n个人资料、情绪记录、统计数据等全部信息。'**
  1684 + String get deleteAccountWarningPrompt;
  1685 +
  1686 + /// No description provided for @deleteAccountWarningNote1.
  1687 + ///
  1688 + /// In zh, this message translates to:
  1689 + /// **'注1:你的健康数据会保存在苹果健康,我们不会删除苹果健康中的数据。'**
  1690 + String get deleteAccountWarningNote1;
  1691 +
  1692 + /// No description provided for @deleteAccountWarningNote2.
  1693 + ///
  1694 + /// In zh, this message translates to:
  1695 + /// **'注2:删除账号不会影响你在App Store的订阅状态,如果需要取消订阅,请在APPs Store - 头像 - 订阅中手动取消订阅。'**
  1696 + String get deleteAccountWarningNote2;
  1697 +
1644 /// No description provided for @confirmDeletion. 1698 /// No description provided for @confirmDeletion.
1645 /// 1699 ///
1646 /// In zh, this message translates to: 1700 /// In zh, this message translates to:
@@ -2829,7 +2883,7 @@ abstract class AppLocalizations { @@ -2829,7 +2883,7 @@ abstract class AppLocalizations {
2829 /// **'双倍感知 压力减半'** 2883 /// **'双倍感知 压力减半'**
2830 String get reportBottomSlogan; 2884 String get reportBottomSlogan;
2831 2885
2832 - /// No description provided for @refundExplanationTitle. 2886 + /// 退费说明
2833 /// 2887 ///
2834 /// In zh, this message translates to: 2888 /// In zh, this message translates to:
2835 /// **'退费说明'** 2889 /// **'退费说明'**
@@ -3068,6 +3122,204 @@ abstract class AppLocalizations { @@ -3068,6 +3122,204 @@ abstract class AppLocalizations {
3068 /// In zh, this message translates to: 3122 /// In zh, this message translates to:
3069 /// **'插图占位'** 3123 /// **'插图占位'**
3070 String get appReviewIllustrationPlaceholder; 3124 String get appReviewIllustrationPlaceholder;
  3125 +
  3126 + /// No description provided for @overallStressLevelToday.
  3127 + ///
  3128 + /// In zh, this message translates to:
  3129 + /// **'今日的综合压力状态'**
  3130 + String get overallStressLevelToday;
  3131 +
  3132 + /// No description provided for @stressLevelsOnThatDay.
  3133 + ///
  3134 + /// In zh, this message translates to:
  3135 + /// **'该日压力状态'**
  3136 + String get stressLevelsOnThatDay;
  3137 +
  3138 + /// No description provided for @noPressureDataAvailableAtThisTime.
  3139 + ///
  3140 + /// In zh, this message translates to:
  3141 + /// **'暂无压力数据'**
  3142 + String get noPressureDataAvailableAtThisTime;
  3143 +
  3144 + /// No description provided for @membersCanViewTheCompleteData.
  3145 + ///
  3146 + /// In zh, this message translates to:
  3147 + /// **'会员可查看完整数据'**
  3148 + String get membersCanViewTheCompleteData;
  3149 +
  3150 + /// No description provided for @unlockNow.
  3151 + ///
  3152 + /// In zh, this message translates to:
  3153 + /// **'立即解锁'**
  3154 + String get unlockNow;
  3155 +
  3156 + /// No description provided for @pressureOverload.
  3157 + ///
  3158 + /// In zh, this message translates to:
  3159 + /// **'压力过载'**
  3160 + String get pressureOverload;
  3161 +
  3162 + /// No description provided for @beMindfulOfStress.
  3163 + ///
  3164 + /// In zh, this message translates to:
  3165 + /// **'注意压力'**
  3166 + String get beMindfulOfStress;
  3167 +
  3168 + /// No description provided for @statusNormal.
  3169 + ///
  3170 + /// In zh, this message translates to:
  3171 + /// **'状态正常'**
  3172 + String get statusNormal;
  3173 +
  3174 + /// No description provided for @inExcellentCondition.
  3175 + ///
  3176 + /// In zh, this message translates to:
  3177 + /// **'状态优秀'**
  3178 + String get inExcellentCondition;
  3179 +
  3180 + /// No description provided for @waitingForData.
  3181 + ///
  3182 + /// In zh, this message translates to:
  3183 + /// **'等待数据'**
  3184 + String get waitingForData;
  3185 +
  3186 + /// No description provided for @pressure.
  3187 + ///
  3188 + /// In zh, this message translates to:
  3189 + /// **'压力'**
  3190 + String get pressure;
  3191 +
  3192 + /// No description provided for @mostRecent.
  3193 + ///
  3194 + /// In zh, this message translates to:
  3195 + /// **'最近一次'**
  3196 + String get mostRecent;
  3197 +
  3198 + /// No description provided for @theDayBeforeYesterday.
  3199 + ///
  3200 + /// In zh, this message translates to:
  3201 + /// **'前天'**
  3202 + String get theDayBeforeYesterday;
  3203 +
  3204 + /// No description provided for @uploadPhotos.
  3205 + ///
  3206 + /// In zh, this message translates to:
  3207 + /// **'相册上传'**
  3208 + String get uploadPhotos;
  3209 +
  3210 + /// No description provided for @filming.
  3211 + ///
  3212 + /// In zh, this message translates to:
  3213 + /// **'拍摄'**
  3214 + String get filming;
  3215 +
  3216 + /// No description provided for @unlockTheProVersion.
  3217 + ///
  3218 + /// In zh, this message translates to:
  3219 + /// **'解锁专业版'**
  3220 + String get unlockTheProVersion;
  3221 +
  3222 + /// No description provided for @embarkOnAJourneyOfStressAwarenessAndWellnessSupport.
  3223 + ///
  3224 + /// In zh, this message translates to:
  3225 + /// **'开启压力预警与健康陪伴之旅'**
  3226 + String get embarkOnAJourneyOfStressAwarenessAndWellnessSupport;
  3227 +
  3228 + /// 分享好友邀请码文案模板
  3229 + ///
  3230 + /// In zh, this message translates to:
  3231 + /// **'嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!\n它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。\n快来一起使用吧 👉 https://doublefeel.cn/\n我的好友id:{inviteCode}'**
  3232 + String sharePartnerCodeTemplate(String inviteCode);
  3233 +
  3234 + /// No description provided for @bindPartnerIdNotExistTitle.
  3235 + ///
  3236 + /// In zh, this message translates to:
  3237 + /// **'该ID不存在'**
  3238 + String get bindPartnerIdNotExistTitle;
  3239 +
  3240 + /// No description provided for @bindPartnerIdNotExistMessage.
  3241 + ///
  3242 + /// In zh, this message translates to:
  3243 + /// **'这个ID不存在哦,请检查后重新输入'**
  3244 + String get bindPartnerIdNotExistMessage;
  3245 +
  3246 + /// No description provided for @bindPartnerDialogGotIt.
  3247 + ///
  3248 + /// In zh, this message translates to:
  3249 + /// **'我知道了'**
  3250 + String get bindPartnerDialogGotIt;
  3251 +
  3252 + /// No description provided for @bindPartnerAddFailedTitle.
  3253 + ///
  3254 + /// In zh, this message translates to:
  3255 + /// **'添加失败'**
  3256 + String get bindPartnerAddFailedTitle;
  3257 +
  3258 + /// No description provided for @bindPartnerAddFailedMessage.
  3259 + ///
  3260 + /// In zh, this message translates to:
  3261 + /// **'该用户不允许被添加好友,无法添加Ta哦'**
  3262 + String get bindPartnerAddFailedMessage;
  3263 +
  3264 + /// No description provided for @bindPartnerAlreadyFriendTitle.
  3265 + ///
  3266 + /// In zh, this message translates to:
  3267 + /// **'对方已经是你的好友啦'**
  3268 + String get bindPartnerAlreadyFriendTitle;
  3269 +
  3270 + /// No description provided for @bindPartnerAlreadyFriendMessage.
  3271 + ///
  3272 + /// In zh, this message translates to:
  3273 + /// **'请不要重复添加哦'**
  3274 + String get bindPartnerAlreadyFriendMessage;
  3275 +
  3276 + /// 好友的状态标题
  3277 + ///
  3278 + /// In zh, this message translates to:
  3279 + /// **'{remarkName}的状态'**
  3280 + String friendStatusTitle(String remarkName);
  3281 +
  3282 + /// No description provided for @annualMemberDiscounts.
  3283 + ///
  3284 + /// In zh, this message translates to:
  3285 + /// **'年度会员优惠'**
  3286 + String get annualMemberDiscounts;
  3287 +
  3288 + /// No description provided for @specialOffers.
  3289 + ///
  3290 + /// In zh, this message translates to:
  3291 + /// **'优惠'**
  3292 + String get specialOffers;
  3293 +
  3294 + /// No description provided for @currentPrice.
  3295 + ///
  3296 + /// In zh, this message translates to:
  3297 + /// **'现价'**
  3298 + String get currentPrice;
  3299 +
  3300 + /// 原价标签
  3301 + ///
  3302 + /// In zh, this message translates to:
  3303 + /// **'原价{price}'**
  3304 + String originalPrice(String price);
  3305 +
  3306 + /// No description provided for @freeRedemptionOffer.
  3307 + ///
  3308 + /// In zh, this message translates to:
  3309 + /// **'免费兑换优惠'**
  3310 + String get freeRedemptionOffer;
  3311 +
  3312 + /// No description provided for @cellPhoneNumber.
  3313 + ///
  3314 + /// In zh, this message translates to:
  3315 + /// **'手机号'**
  3316 + String get cellPhoneNumber;
  3317 +
  3318 + /// No description provided for @todayOnWeeklyCalendar.
  3319 + ///
  3320 + /// In zh, this message translates to:
  3321 + /// **'今'**
  3322 + String get todayOnWeeklyCalendar;
3071 } 3323 }
3072 3324
3073 class _AppLocalizationsDelegate 3325 class _AppLocalizationsDelegate
@@ -879,6 +879,10 @@ class AppLocalizationsEn extends AppLocalizations { @@ -879,6 +879,10 @@ class AppLocalizationsEn extends AppLocalizations {
879 'If you would like us to reply, please provide your email address'; 879 'If you would like us to reply, please provide your email address';
880 880
881 @override 881 @override
  882 + String get feedbackHintText =>
  883 + '1. Please describe the screen and scenario where the issue occurred\n2. Please provide screenshots to help us resolve the issue more efficiently\n3. Please leave your contact information so we can get back to you as soon as possible';
  884 +
  885 + @override
882 String get uploadProof => 'Upload Proof'; 886 String get uploadProof => 'Upload Proof';
883 887
884 @override 888 @override
@@ -901,6 +905,34 @@ class AppLocalizationsEn extends AppLocalizations { @@ -901,6 +905,34 @@ class AppLocalizationsEn extends AppLocalizations {
901 String get logOut => 'Log Out'; 905 String get logOut => 'Log Out';
902 906
903 @override 907 @override
  908 + String get confirmLogoutPrompt => 'Are you sure you want to log out?';
  909 +
  910 + @override
  911 + String get confirmLogout => 'Log Out';
  912 +
  913 + @override
  914 + String get loggedOutSuccessfully => 'Logged out successfully';
  915 +
  916 + @override
  917 + String get accountDeletedSuccessfully => 'Account deleted successfully';
  918 +
  919 + @override
  920 + String get deleteAccountWarningTitle =>
  921 + 'Once deleted, the account cannot be recovered! Please proceed with caution.';
  922 +
  923 + @override
  924 + String get deleteAccountWarningPrompt =>
  925 + 'Note: Deleting the account will remove all information in this account, including but not limited to\npersonal profile, mood records, and statistical data.';
  926 +
  927 + @override
  928 + String get deleteAccountWarningNote1 =>
  929 + 'Note 1: Your health data will be saved in Apple Health, and we will not delete data from Apple Health.';
  930 +
  931 + @override
  932 + String get deleteAccountWarningNote2 =>
  933 + 'Note 2: Deleting the account will not affect your subscription status in the App Store. If you need to cancel the subscription, please cancel it manually in the App Store -> Profile -> Subscriptions.';
  934 +
  935 + @override
904 String get confirmDeletion => 'Confirm Deletion'; 936 String get confirmDeletion => 'Confirm Deletion';
905 937
906 @override 938 @override
@@ -1708,4 +1740,114 @@ class AppLocalizationsEn extends AppLocalizations { @@ -1708,4 +1740,114 @@ class AppLocalizationsEn extends AppLocalizations {
1708 1740
1709 @override 1741 @override
1710 String get appReviewIllustrationPlaceholder => 'Illustration Placeholder'; 1742 String get appReviewIllustrationPlaceholder => 'Illustration Placeholder';
  1743 +
  1744 + @override
  1745 + String get overallStressLevelToday => 'Overall Stress Level Today';
  1746 +
  1747 + @override
  1748 + String get stressLevelsOnThatDay => 'Stress Levels on That Day';
  1749 +
  1750 + @override
  1751 + String get noPressureDataAvailableAtThisTime =>
  1752 + 'No pressure data available at this time';
  1753 +
  1754 + @override
  1755 + String get membersCanViewTheCompleteData =>
  1756 + 'Members can view the complete data';
  1757 +
  1758 + @override
  1759 + String get unlockNow => 'Unlock Now';
  1760 +
  1761 + @override
  1762 + String get pressureOverload => 'Pressure Overload';
  1763 +
  1764 + @override
  1765 + String get beMindfulOfStress => 'Be Aware of Stress';
  1766 +
  1767 + @override
  1768 + String get statusNormal => 'Status: Normal';
  1769 +
  1770 + @override
  1771 + String get inExcellentCondition => 'In excellent condition';
  1772 +
  1773 + @override
  1774 + String get waitingForData => 'Waiting for data';
  1775 +
  1776 + @override
  1777 + String get pressure => 'Pressure';
  1778 +
  1779 + @override
  1780 + String get mostRecent => 'Most recent';
  1781 +
  1782 + @override
  1783 + String get theDayBeforeYesterday => 'The day before yesterday';
  1784 +
  1785 + @override
  1786 + String get uploadPhotos => 'Upload Photos';
  1787 +
  1788 + @override
  1789 + String get filming => 'Filming';
  1790 +
  1791 + @override
  1792 + String get unlockTheProVersion => 'Unlock the Pro Version';
  1793 +
  1794 + @override
  1795 + String get embarkOnAJourneyOfStressAwarenessAndWellnessSupport =>
  1796 + 'Embark on a Journey of Stress Awareness and Wellness Support';
  1797 +
  1798 + @override
  1799 + String sharePartnerCodeTemplate(String inviteCode) {
  1800 + 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';
  1801 + }
  1802 +
  1803 + @override
  1804 + String get bindPartnerIdNotExistTitle => 'ID does not exist';
  1805 +
  1806 + @override
  1807 + String get bindPartnerIdNotExistMessage =>
  1808 + 'This ID does not exist. Please check and try again';
  1809 +
  1810 + @override
  1811 + String get bindPartnerDialogGotIt => 'Got it';
  1812 +
  1813 + @override
  1814 + String get bindPartnerAddFailedTitle => 'Failed to add';
  1815 +
  1816 + @override
  1817 + String get bindPartnerAddFailedMessage =>
  1818 + 'This user does not allow adding friends, cannot add them';
  1819 +
  1820 + @override
  1821 + String get bindPartnerAlreadyFriendTitle => 'They are already your friend';
  1822 +
  1823 + @override
  1824 + String get bindPartnerAlreadyFriendMessage => 'Please do not add again';
  1825 +
  1826 + @override
  1827 + String friendStatusTitle(String remarkName) {
  1828 + return '$remarkName\'s Status';
  1829 + }
  1830 +
  1831 + @override
  1832 + String get annualMemberDiscounts => 'Annual Member Discounts';
  1833 +
  1834 + @override
  1835 + String get specialOffers => 'Special Offers';
  1836 +
  1837 + @override
  1838 + String get currentPrice => 'Current Price';
  1839 +
  1840 + @override
  1841 + String originalPrice(String price) {
  1842 + return 'Original price $price';
  1843 + }
  1844 +
  1845 + @override
  1846 + String get freeRedemptionOffer => 'Free Redemption Offer';
  1847 +
  1848 + @override
  1849 + String get cellPhoneNumber => 'Cell phone number';
  1850 +
  1851 + @override
  1852 + String get todayOnWeeklyCalendar => 'today';
1711 } 1853 }
@@ -818,6 +818,10 @@ class AppLocalizationsZh extends AppLocalizations { @@ -818,6 +818,10 @@ class AppLocalizationsZh extends AppLocalizations {
818 '如果需要我们回复,请填写联系邮箱'; 818 '如果需要我们回复,请填写联系邮箱';
819 819
820 @override 820 @override
  821 + String get feedbackHintText =>
  822 + '1.请说明问题的界面和使用场景\n2.请提供截图,以便提升解决问题效率\n3.请留下联系方式,我们尽快回复联系您';
  823 +
  824 + @override
821 String get uploadProof => '上传凭证'; 825 String get uploadProof => '上传凭证';
822 826
823 @override 827 @override
@@ -839,6 +843,32 @@ class AppLocalizationsZh extends AppLocalizations { @@ -839,6 +843,32 @@ class AppLocalizationsZh extends AppLocalizations {
839 String get logOut => '退出登录'; 843 String get logOut => '退出登录';
840 844
841 @override 845 @override
  846 + String get confirmLogoutPrompt => '确认退出登录吗?';
  847 +
  848 + @override
  849 + String get confirmLogout => '确认退出';
  850 +
  851 + @override
  852 + String get loggedOutSuccessfully => '已退出登录';
  853 +
  854 + @override
  855 + String get accountDeletedSuccessfully => '账号注销成功';
  856 +
  857 + @override
  858 + String get deleteAccountWarningTitle => '注销账号后将无法找回!请谨慎操作';
  859 +
  860 + @override
  861 + String get deleteAccountWarningPrompt =>
  862 + '提示:注销账号将会删除该账号内包括但不限于\n个人资料、情绪记录、统计数据等全部信息。';
  863 +
  864 + @override
  865 + String get deleteAccountWarningNote1 => '注1:你的健康数据会保存在苹果健康,我们不会删除苹果健康中的数据。';
  866 +
  867 + @override
  868 + String get deleteAccountWarningNote2 =>
  869 + '注2:删除账号不会影响你在App Store的订阅状态,如果需要取消订阅,请在APPs Store - 头像 - 订阅中手动取消订阅。';
  870 +
  871 + @override
842 String get confirmDeletion => '确认注销'; 872 String get confirmDeletion => '确认注销';
843 873
844 @override 874 @override
@@ -1615,4 +1645,110 @@ class AppLocalizationsZh extends AppLocalizations { @@ -1615,4 +1645,110 @@ class AppLocalizationsZh extends AppLocalizations {
1615 1645
1616 @override 1646 @override
1617 String get appReviewIllustrationPlaceholder => '插图占位'; 1647 String get appReviewIllustrationPlaceholder => '插图占位';
  1648 +
  1649 + @override
  1650 + String get overallStressLevelToday => '今日的综合压力状态';
  1651 +
  1652 + @override
  1653 + String get stressLevelsOnThatDay => '该日压力状态';
  1654 +
  1655 + @override
  1656 + String get noPressureDataAvailableAtThisTime => '暂无压力数据';
  1657 +
  1658 + @override
  1659 + String get membersCanViewTheCompleteData => '会员可查看完整数据';
  1660 +
  1661 + @override
  1662 + String get unlockNow => '立即解锁';
  1663 +
  1664 + @override
  1665 + String get pressureOverload => '压力过载';
  1666 +
  1667 + @override
  1668 + String get beMindfulOfStress => '注意压力';
  1669 +
  1670 + @override
  1671 + String get statusNormal => '状态正常';
  1672 +
  1673 + @override
  1674 + String get inExcellentCondition => '状态优秀';
  1675 +
  1676 + @override
  1677 + String get waitingForData => '等待数据';
  1678 +
  1679 + @override
  1680 + String get pressure => '压力';
  1681 +
  1682 + @override
  1683 + String get mostRecent => '最近一次';
  1684 +
  1685 + @override
  1686 + String get theDayBeforeYesterday => '前天';
  1687 +
  1688 + @override
  1689 + String get uploadPhotos => '相册上传';
  1690 +
  1691 + @override
  1692 + String get filming => '拍摄';
  1693 +
  1694 + @override
  1695 + String get unlockTheProVersion => '解锁专业版';
  1696 +
  1697 + @override
  1698 + String get embarkOnAJourneyOfStressAwarenessAndWellnessSupport =>
  1699 + '开启压力预警与健康陪伴之旅';
  1700 +
  1701 + @override
  1702 + String sharePartnerCodeTemplate(String inviteCode) {
  1703 + return '嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!\n它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。\n快来一起使用吧 👉 https://doublefeel.cn/\n我的好友id:$inviteCode';
  1704 + }
  1705 +
  1706 + @override
  1707 + String get bindPartnerIdNotExistTitle => '该ID不存在';
  1708 +
  1709 + @override
  1710 + String get bindPartnerIdNotExistMessage => '这个ID不存在哦,请检查后重新输入';
  1711 +
  1712 + @override
  1713 + String get bindPartnerDialogGotIt => '我知道了';
  1714 +
  1715 + @override
  1716 + String get bindPartnerAddFailedTitle => '添加失败';
  1717 +
  1718 + @override
  1719 + String get bindPartnerAddFailedMessage => '该用户不允许被添加好友,无法添加Ta哦';
  1720 +
  1721 + @override
  1722 + String get bindPartnerAlreadyFriendTitle => '对方已经是你的好友啦';
  1723 +
  1724 + @override
  1725 + String get bindPartnerAlreadyFriendMessage => '请不要重复添加哦';
  1726 +
  1727 + @override
  1728 + String friendStatusTitle(String remarkName) {
  1729 + return '$remarkName的状态';
  1730 + }
  1731 +
  1732 + @override
  1733 + String get annualMemberDiscounts => '年度会员优惠';
  1734 +
  1735 + @override
  1736 + String get specialOffers => '优惠';
  1737 +
  1738 + @override
  1739 + String get currentPrice => '现价';
  1740 +
  1741 + @override
  1742 + String originalPrice(String price) {
  1743 + return '原价$price';
  1744 + }
  1745 +
  1746 + @override
  1747 + String get freeRedemptionOffer => '免费兑换优惠';
  1748 +
  1749 + @override
  1750 + String get cellPhoneNumber => '手机号';
  1751 +
  1752 + @override
  1753 + String get todayOnWeeklyCalendar => '今';
1618 } 1754 }