import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../services/odoo_service.dart'; import '../services/notification_service.dart'; import '../theme/app_theme.dart'; import 'notification_detail_screen.dart'; class NotificationsScreen extends StatefulWidget { const NotificationsScreen({super.key}); @override State createState() => _NotificationsScreenState(); } class _NotificationsScreenState extends State { List _notifications = []; List _readIds = []; bool _isLoading = true; @override void initState() { super.initState(); _fetchNotifications(); } Future _fetchNotifications() async { try { final client = OdooService().client; if (client == null) throw Exception('Not connected'); final response = await client.callRPC( '/api/loyalty/fetch_notifications', 'call', {'last_id': 0}, ); if (response != null && response['status'] == 'success') { final List fetched = response['data'] ?? []; final prefs = await SharedPreferences.getInstance(); final readIds = prefs.getStringList('read_notification_ids') ?? []; if (mounted) { setState(() { _notifications = fetched; _readIds = readIds; _isLoading = false; }); } } else { throw Exception('Invalid response from server'); } } catch (e) { if (mounted) { setState(() => _isLoading = false); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error loading notifications: $e')), ); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Notifications')), body: _isLoading ? const Center(child: CircularProgressIndicator()) : _notifications.isEmpty ? Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.notifications_none, size: 64, color: AppTheme.onSurfaceVariant.withValues(alpha: 0.4), ), const SizedBox(height: 16), Text( 'No notifications yet.', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: AppTheme.onSurfaceVariant, ), ), ], ), ) : RefreshIndicator( onRefresh: _fetchNotifications, child: ListView.separated( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 20), itemCount: _notifications.length, separatorBuilder: (_, __) => const SizedBox(height: 8), itemBuilder: (context, index) { final notif = _notifications[index]; final isUnread = !_readIds.contains((notif['id'] as int? ?? 0).toString()); return _NotificationCard( notif: notif, isUnread: isUnread, onTap: () async { final prefs = await SharedPreferences.getInstance(); final readIds = prefs.getStringList('read_notification_ids') ?? []; final notifIdStr = (notif['id'] as int? ?? 0).toString(); if (!readIds.contains(notifIdStr)) { readIds.add(notifIdStr); await prefs.setStringList('read_notification_ids', readIds); // Recalculate and update system badge count final unreadCount = _notifications .where((n) => !readIds.contains((n['id'] as int? ?? 0).toString())) .length; await NotificationService().setBadge(unreadCount); } if (!mounted) return; await Navigator.push( context, MaterialPageRoute( builder: (_) => NotificationDetailScreen(notif: notif), ), ); _fetchNotifications(); }, ); }, ), ), ); } } class _NotificationCard extends StatelessWidget { final dynamic notif; final bool isUnread; final VoidCallback onTap; const _NotificationCard({ required this.notif, required this.isUnread, required this.onTap, }); @override Widget build(BuildContext context) { final title = notif['title'] as String? ?? 'Notice'; return Material( color: AppTheme.surfaceContainerLow, child: InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 18), child: Row( children: [ notif['has_image'] == true && notif['image_128'] != null ? Container( width: 42, height: 42, decoration: BoxDecoration( shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(4), image: DecorationImage( image: MemoryImage( base64Decode(notif['image_128'] as String), ), fit: BoxFit.cover, ), ), ) : Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: AppTheme.primary.withValues(alpha: 0.15), shape: BoxShape.rectangle, ), child: const Icon( Icons.campaign_outlined, color: AppTheme.secondary, size: 22, ), ), const SizedBox(width: 16), Expanded( child: Text( title, style: Theme.of(context).textTheme.titleSmall?.copyWith( color: AppTheme.onSurface, fontWeight: isUnread ? FontWeight.bold : FontWeight.normal, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 8), if (isUnread) ...[ Container( width: 8, height: 8, decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), ), const SizedBox(width: 8), ], const Icon( Icons.chevron_right, color: AppTheme.onSurfaceVariant, size: 20, ), ], ), ), ), ); } }