Commit e74b78ecc6ba26b00fa673679f096047c226ec46

Authored by 常守达
1 parent 6e4ca7db

fix(today):自动更新今天,通知banner

... ... @@ -104,8 +104,11 @@ class TodayController extends GetMaterialController {
// ── HRV 表盘引导 Banner ───────────────────
final showHrvAdBanner = false.obs;
final showPartnerAdBanner = false.obs;
final showNotificationAuthorizationBanner = false.obs;
void dismissHrvAdBanner() => showHrvAdBanner.value = false;
void dismissNotificationAuthorizationBanner() =>
showNotificationAuthorizationBanner.value = false;
Future<void> dismissPartnerAdBanner() async {
final userId =
... ... @@ -160,6 +163,7 @@ class TodayController extends GetMaterialController {
checkAddFriendVisible();
checkHrvAdBannerVisible();
checkHealthDataAuthCardVisible();
checkNotificationAuthorizationBannerVisible();
checkNotificationCardVisible();
if (!Get.find<UserStateService>().isVip) {
getProductList();
... ... @@ -196,6 +200,24 @@ class TodayController extends GetMaterialController {
}
}
Future<void> checkNotificationAuthorizationBannerVisible() async {
if (isFriend) {
showNotificationAuthorizationBanner.value = false;
} else {
try {
Permission.notification.status.then((status) {
if (status.isGranted) {
showNotificationAuthorizationBanner.value = false;
} else {
showNotificationAuthorizationBanner.value = true;
}
});
} on Exception catch (e) {
AppLogger.e(e);
}
}
}
void checkAddFriendVisible() {
if (isFriend) {
showPartnerAdBanner.value = false;
... ... @@ -504,6 +526,7 @@ class TodayController extends GetMaterialController {
hrvAnnotations.clear();
v2HealthData.value = null;
v2ActivityTarget.value = null;
// v2StressScore.value = null;
}
... ... @@ -697,12 +720,13 @@ class TodayController extends GetMaterialController {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed) {
// var today = DateUtils.dateOnly(DateTime.now());
// lastSelectableDay.value = today;
// changeDate(today);
var today = DateUtils.dateOnly(DateTime.now());
lastSelectableDay.value = today;
changeDate(today);
checkAddFriendVisible();
checkHrvAdBannerVisible();
checkHealthDataAuthCardVisible();
checkNotificationAuthorizationBannerVisible();
_performHealthDataUpload();
// _startCoreCaculate();
// unawaited(loadDataForDate(selectedDate.value));
... ... @@ -720,7 +744,33 @@ class TodayController extends GetMaterialController {
checkAddFriendVisible();
checkHrvAdBannerVisible();
checkHealthDataAuthCardVisible(isPullToRefresh: isPullToRefresh);
checkNotificationAuthorizationBannerVisible();
_performHealthDataUpload();
// await loadDataForDate(selectedDate.value);
}
Future<void> requestNotificationAuthorization() async {
try {
final status = await Permission.notification.status;
if (status.isGranted) {
// 已授权,无需再请求
return;
}
if (status.isPermanentlyDenied) {
// 用户永久拒绝(Android),引导去设置
await openAppSettings();
return;
}
// 未决定 → 弹出系统授权弹窗
final result = await Permission.notification.request();
AppLogger.d(result.isGranted
? 'Notification authorization granted'
: 'Notification authorization skipped');
} catch (error) {
AppLogger.e('Notification authorization failed: $error');
}
}
}
... ...
... ... @@ -55,6 +55,54 @@ class TodayHrvAdBanner extends StatelessWidget {
}
}
class TodayNotificationAuthorizationBanner extends StatelessWidget {
const TodayNotificationAuthorizationBanner({
super.key,
required this.controller,
this.showMargin = true,
});
final TodayController controller;
final bool showMargin;
@override
Widget build(BuildContext context) {
return Obx(() {
if (!controller.showHrvAdBanner.value) return const SizedBox.shrink();
return GestureDetector(
onTap: () async {
ta.track('click_doublefeel_today_status',
properties: {'ita': '点击开启通知权限'});
await controller.requestNotificationAuthorization();
controller.checkNotificationAuthorizationBannerVisible();
},
child: _TodayGuideBanner(
title: context.l10n.turnOnNotifications,
subtitle:
context.l10n.stayUpToDateOnChangesInYourOwnAndYourFriendsHealth,
right: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: Image.asset(
'assets/images/common/ic_arrow_forward.png',
width: 20,
height: 20,
color: context.colors.primary,
),
),
leftImagePath: 'assets/images/today/ic_banner_notify_permission.png',
onClose: controller.dismissHrvAdBanner,
showMargin: showMargin,
),
);
});
}
}
class TodayPartnerAdBanner extends StatelessWidget {
const TodayPartnerAdBanner({
super.key,
... ... @@ -213,6 +261,8 @@ class TodayAdCarousel extends StatelessWidget {
Widget build(BuildContext context) {
return Obx(() {
final showHrv = controller.showHrvAdBanner.value;
final showNotificationAuthorization =
controller.showNotificationAuthorizationBanner.value;
final showPartner = controller.showPartnerAdBanner.value &&
!controller.userStateService.isBound;
... ... @@ -225,6 +275,14 @@ class TodayAdCarousel extends StatelessWidget {
),
);
}
if (showNotificationAuthorization) {
activeBanners.add(
TodayNotificationAuthorizationBanner(
controller: controller,
showMargin: false,
),
);
}
if (showPartner) {
activeBanners.add(
TodayPartnerAdBanner(
... ...
... ... @@ -201,7 +201,7 @@ class V2StressScore {
factory V2StressScore.fromJson(Map<String, dynamic> json) {
return V2StressScore(
state: _parseInt(json['state']),
state: _parseInt(json['state']) ?? 0,
hrvScore: _parseInt(json['hrv_score']),
hrScore: _parseInt(json['hr_score']),
latestHr: _parseInt(json['latest_hr']),
... ...
... ... @@ -710,5 +710,7 @@
"latestHrvTipAttentionAboveBaseline": "Your HRV is on the low side. Consider relaxing, keeping regular rest, and paying attention to nutrition and recovery.",
"latestHrvTipAttentionBelowBaseline": "Your HRV is clearly below your usual level. Recent stress may be elevated, so try to rest and adjust your state.",
"latestHrvTipOverloadAboveBaseline": "Your HRV is at a relatively low level. Your body may be under higher stress. If this is after exercise, a lower HRV can be normal. Rest and recover in time.",
"latestHrvTipOverloadBelowBaseline": "Your HRV is clearly below your usual level. Your body may be under high stress. If this is after exercise, a lower HRV can be normal. Reduce exertion, rest in time, and support sleep recovery."
}
"latestHrvTipOverloadBelowBaseline": "Your HRV is clearly below your usual level. Your body may be under high stress. If this is after exercise, a lower HRV can be normal. Reduce exertion, rest in time, and support sleep recovery.",
"turnOnNotifications": "Turn on Notifications",
"stayUpToDateOnChangesInYourOwnAndYourFriendsHealth": "Stay up to date on changes in your own and your friends' health"
}
\ No newline at end of file
... ...
... ... @@ -1103,5 +1103,7 @@
"latestHrvTipAttentionAboveBaseline": "你的 HRV 偏低,建议适当放松、规律休息,并注意饮食与恢复。",
"latestHrvTipAttentionBelowBaseline": "你的 HRV 明显低于日常水平,近期可能压力偏高,建议尽量休息与调整状态。",
"latestHrvTipOverloadAboveBaseline": "你的 HRV 处于较低水平,身体可能正在承受较高压力(刚运动完 HRV 降低则属于正常情况),建议及时休息与恢复。",
"latestHrvTipOverloadBelowBaseline": "你的 HRV 明显低于平时水平,身体可能处于高压力状态(刚运动完 HRV 降低则属于正常情况),建议减少消耗、及时休息,并保证睡眠恢复。"
}
"latestHrvTipOverloadBelowBaseline": "你的 HRV 明显低于平时水平,身体可能处于高压力状态(刚运动完 HRV 降低则属于正常情况),建议减少消耗、及时休息,并保证睡眠恢复。",
"turnOnNotifications": "开启通知",
"stayUpToDateOnChangesInYourOwnAndYourFriendsHealth": "及时了解自己和好友的健康波动"
}
\ No newline at end of file
... ...
... ... @@ -4082,6 +4082,18 @@ abstract class AppLocalizations {
/// In zh, this message translates to:
/// **'你的 HRV 明显低于平时水平,身体可能处于高压力状态(刚运动完 HRV 降低则属于正常情况),建议减少消耗、及时休息,并保证睡眠恢复。'**
String get latestHrvTipOverloadBelowBaseline;
/// No description provided for @turnOnNotifications.
///
/// In zh, this message translates to:
/// **'开启通知'**
String get turnOnNotifications;
/// No description provided for @stayUpToDateOnChangesInYourOwnAndYourFriendsHealth.
///
/// In zh, this message translates to:
/// **'及时了解自己和好友的健康波动'**
String get stayUpToDateOnChangesInYourOwnAndYourFriendsHealth;
}
class _AppLocalizationsDelegate
... ...
... ... @@ -2288,4 +2288,11 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get latestHrvTipOverloadBelowBaseline =>
'Your HRV is clearly below your usual level. Your body may be under high stress. If this is after exercise, a lower HRV can be normal. Reduce exertion, rest in time, and support sleep recovery.';
@override
String get turnOnNotifications => 'Turn on Notifications';
@override
String get stayUpToDateOnChangesInYourOwnAndYourFriendsHealth =>
'Stay up to date on changes in your own and your friends\' health';
}
... ...
... ... @@ -2184,4 +2184,11 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get latestHrvTipOverloadBelowBaseline =>
'你的 HRV 明显低于平时水平,身体可能处于高压力状态(刚运动完 HRV 降低则属于正常情况),建议减少消耗、及时休息,并保证睡眠恢复。';
@override
String get turnOnNotifications => '开启通知';
@override
String get stayUpToDateOnChangesInYourOwnAndYourFriendsHealth =>
'及时了解自己和好友的健康波动';
}
... ...