Fix notification delivery and settings flows
This commit is contained in:
@@ -10,6 +10,19 @@ import '../../../data/services/notification_analytics_service.dart';
|
||||
import '../../../data/services/notification_inbox_service.dart';
|
||||
import '../../../data/services/notification_service.dart';
|
||||
|
||||
int _todayUpcomingAlarmCount(List<NotificationPendingAlert> alarms) {
|
||||
final now = DateTime.now();
|
||||
final todayStart = DateTime(now.year, now.month, now.day);
|
||||
final todayEnd = todayStart.add(const Duration(days: 1));
|
||||
return alarms.where((alarm) {
|
||||
final when = alarm.scheduledAt;
|
||||
if (when == null) return false;
|
||||
return !when.isBefore(now) &&
|
||||
!when.isBefore(todayStart) &&
|
||||
when.isBefore(todayEnd);
|
||||
}).length;
|
||||
}
|
||||
|
||||
class NotificationCenterScreen extends StatefulWidget {
|
||||
const NotificationCenterScreen({super.key});
|
||||
|
||||
@@ -56,6 +69,14 @@ class _NotificationCenterScreenState extends State<NotificationCenterScreen>
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _clearNonCritical() async {
|
||||
await NotificationInboxService.instance.clearNonCritical();
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Notifikasi non-kritis dibersihkan.')),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
@@ -91,10 +112,19 @@ class _NotificationCenterScreenState extends State<NotificationCenterScreen>
|
||||
builder: (context, _, __) {
|
||||
final unread =
|
||||
NotificationInboxService.instance.unreadCount();
|
||||
if (unread <= 0) return const SizedBox.shrink();
|
||||
return TextButton(
|
||||
onPressed: _markAllRead,
|
||||
child: const Text('Tandai semua'),
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: _clearNonCritical,
|
||||
child: const Text('Bersihkan'),
|
||||
),
|
||||
if (unread > 0)
|
||||
TextButton(
|
||||
onPressed: _markAllRead,
|
||||
child: const Text('Tandai semua'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -113,7 +143,9 @@ class _NotificationCenterScreenState extends State<NotificationCenterScreen>
|
||||
FutureBuilder<List<NotificationPendingAlert>>(
|
||||
future: _alarmsFuture,
|
||||
builder: (context, snapshot) {
|
||||
final count = snapshot.data?.length ?? 0;
|
||||
final count = _todayUpcomingAlarmCount(
|
||||
snapshot.data ?? const <NotificationPendingAlert>[],
|
||||
);
|
||||
return Tab(text: count > 0 ? 'Alarm ($count)' : 'Alarm');
|
||||
},
|
||||
),
|
||||
@@ -170,13 +202,21 @@ class _AlarmTabState extends State<_AlarmTab> {
|
||||
|
||||
final alarms = snapshot.data ?? const <NotificationPendingAlert>[];
|
||||
final now = DateTime.now();
|
||||
final upcoming = alarms
|
||||
.where((alarm) =>
|
||||
alarm.scheduledAt == null || !alarm.scheduledAt!.isBefore(now))
|
||||
.toList();
|
||||
final todayStart = DateTime(now.year, now.month, now.day);
|
||||
final todayEnd = todayStart.add(const Duration(days: 1));
|
||||
final upcoming = alarms.where((alarm) {
|
||||
final when = alarm.scheduledAt;
|
||||
if (when == null) return false;
|
||||
return !when.isBefore(now) &&
|
||||
!when.isBefore(todayStart) &&
|
||||
when.isBefore(todayEnd);
|
||||
}).toList();
|
||||
final passed = alarms
|
||||
.where((alarm) =>
|
||||
alarm.scheduledAt != null && alarm.scheduledAt!.isBefore(now))
|
||||
alarm.scheduledAt != null &&
|
||||
alarm.scheduledAt!.isBefore(now) &&
|
||||
!alarm.scheduledAt!.isBefore(todayStart) &&
|
||||
alarm.scheduledAt!.isBefore(todayEnd))
|
||||
.toList();
|
||||
final visible = _alarmFilter == 'past' ? passed : upcoming;
|
||||
|
||||
@@ -254,7 +294,7 @@ class _AlarmTabState extends State<_AlarmTab> {
|
||||
),
|
||||
child: Text(
|
||||
_alarmFilter == 'upcoming'
|
||||
? 'Tidak ada alarm akan datang.'
|
||||
? 'Tidak ada alarm tersisa untuk hari ini.'
|
||||
: 'Belum ada alarm sudah lewat.',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
@@ -368,9 +408,7 @@ class _AlarmTabState extends State<_AlarmTab> {
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
_FilterChip(
|
||||
label: upcomingCount > 0
|
||||
? 'Akan Datang ($upcomingCount)'
|
||||
: 'Akan Datang',
|
||||
label: upcomingCount > 0 ? 'Hari Ini ($upcomingCount)' : 'Hari Ini',
|
||||
selected: _alarmFilter == 'upcoming',
|
||||
isDark: isDark,
|
||||
onTap: () => setState(() => _alarmFilter = 'upcoming'),
|
||||
@@ -627,6 +665,16 @@ class _InboxTabState extends State<_InboxTab> {
|
||||
label: _inboxLabel(item.type),
|
||||
color: accent,
|
||||
),
|
||||
if ((item.meta['isGrouped'] == true) &&
|
||||
(item.meta['groupCount'] is num))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: _TypeBadge(
|
||||
label:
|
||||
'${(item.meta['groupCount'] as num).toInt()} item',
|
||||
color: AppColors.sage,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
_formatInboxTime(item.createdAt),
|
||||
@@ -694,6 +742,12 @@ class _InboxTabState extends State<_InboxTab> {
|
||||
isDark: isDark,
|
||||
onTap: () => setState(() => _filter = 'system'),
|
||||
),
|
||||
_FilterChip(
|
||||
label: 'Kritis',
|
||||
selected: _filter == 'critical',
|
||||
isDark: isDark,
|
||||
onTap: () => setState(() => _filter = 'critical'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user