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'; import '../utils/safe_cast.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 { if (OdooService().client == null) throw Exception('Not connected'); final response = await OdooService().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 partnerId = OdooService().client?.sessionId?.partnerId ?? 0; final keyReadNotificationIds = 'read_notification_ids_$partnerId'; final readIds = prefs.getStringList(keyReadNotificationIds) ?? []; 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()) : RefreshIndicator( onRefresh: _fetchNotifications, child: _notifications.isEmpty ? CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverFillRemaining( hasScrollBody: false, child: 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, ), ), ], ), ), ), ], ) : 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 partnerId = OdooService().client?.sessionId?.partnerId ?? 0; final keyReadNotificationIds = 'read_notification_ids_$partnerId'; final readIds = prefs.getStringList(keyReadNotificationIds) ?? []; final notifIdStr = (notif['id'] as int? ?? 0) .toString(); if (!readIds.contains(notifIdStr)) { readIds.add(notifIdStr); await prefs.setStringList( keyReadNotificationIds, 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 (!context.mounted) return; await Navigator.push( context, MaterialPageRoute( builder: (_) => NotificationDetailScreen(notif: notif), ), ); if (!context.mounted) return; _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 = safeString(notif['title']) ?? '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, ), ], ), ), ), ); } }