uaestats_fe/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification.dart
2026-05-25 18:01:41 +05:30

578 lines
19 KiB
Dart
Executable File

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/connectivity_provider.dart';
import 'package:uae_stat/config/theme/service.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import '../../custom_drawer_routes.dart';
import 'package:http/http.dart' as http;
import 'package:uae_stat/l10n/app_localizations.dart';
class NotificationPage extends ConsumerStatefulWidget {
@override
_NotificationPageState createState() => _NotificationPageState();
}
class _NotificationPageState extends ConsumerState<NotificationPage> {
// Example notification data
final _pb = PocketBase(apiUrl);
bool isLoading = true;
List<Map<String, dynamic>> notifications = [];
List<String> userReadedNotiOrNot = [];
String notificationCount = '';
List<dynamic> homePageData = [];
List<dynamic> filteredData = [];
// Current selected tab index
int selectedTabIndex = 0;
late final Locale locale;
dynamic userID;
late UserService _userService;
late ThemeMode currentTheme;
// Tab categories
// final List<String> tabs = [
// "All updates",
// "App updates",
// "Social",
// "Economy",
// "Environment",
// "Favourites"
// ];
@override
void initState() {
super.initState();
locale = ref.read(localeProvider) ?? const Locale('en');
_userService = UserService();
_initializePage();
currentTheme = ref.read(themeProvider);
WidgetsBinding.instance.addPostFrameCallback((_) {
final isDark = currentTheme == ThemeMode.dark ||
(currentTheme == ThemeMode.system &&
MediaQuery.of(context).platformBrightness == Brightness.dark);
final themeMode = (isDark ? 'dark' : 'light');
fetchData(locale?.languageCode ?? 'en', themeMode);
});
}
Future<void> fetchData(locale, themeMode) async {
const baseUrl = '${apiUrl}api/getUAENumbersData';
try {
final response = await http.get(
Uri.parse(baseUrl + '?language=$locale&color_mode=$themeMode'),
headers: {'APP_SIGNATURE': 'fcsc.gov.ae.X7pL9qZm2A'});
if (response.statusCode == 200) {
setState(() {
homePageData = (json.decode(response.body) as List)
.map((e) => Map<String, dynamic>.from(e))
.toList();
filteredData = List.from(homePageData);
isLoading = false;
});
} else {
throw Exception('Failed to load data');
}
} catch (e) {
setState(() {
isLoading = false;
});
}
}
Future<void> _initializePage() async {
final prefs = await SharedPreferences.getInstance();
userReadedNotiOrNot = prefs.getStringList('read_notification_ids') ?? [];
await fetchNotifications(locale.languageCode);
_processNotifications(notifications, userReadedNotiOrNot);
}
void _processNotifications(
List<Map<String, dynamic>> notification, List<String> push) {
int count = 0;
for (var product in notification) {
if (push.contains(product['id'])) {
count++;
}
}
if (count > 0) {
notificationCount = '($count)';
} else {
notificationCount = '';
}
}
String formatDate(String dateString) {
try {
DateTime dateTime =
DateTime.parse(dateString); // Convert API date to DateTime
return DateFormat('dd MMM yyyy hh:mm a')
.format(dateTime); // Format DateTime
} catch (e) {
return 'Invalid Date'; // Handle parsing errors
}
}
Future<void> fetchNotifications(locale) async {
final baseUrl = apiUrl + 'api/getNotification';
try {
final response = await http.get(Uri.parse('$baseUrl?language=$locale'),
headers: {'APP_SIGNATURE': 'fcsc.gov.ae.X7pL9qZm2A'});
if (response.statusCode == 200) {
final Map<String, dynamic> jsonResponse =
jsonDecode(response.body); // Decode JSON
if (jsonResponse['message'] == 'Success') {
setState(() {
notifications = List<Map<String, dynamic>>.from(
jsonResponse['data']);
isLoading = false;
});
} else {
setState(() {
isLoading = false;
});
throw Exception('Failed to load data');
}
} else {
setState(() {
isLoading = false;
});
throw Exception(
'Failed to load data with status code ${response.statusCode}');
}
} catch (e) {
setState(() {
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
ref.listen<AsyncValue<bool>>(connectivityProvider, (previous, hasInternet) {
if (hasInternet.value == false) {
context.push('/internetcheck');
}
});
// ref.listen<Locale?>(localeProvider, (previous, next) async {
// final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
// setState(() {
// isLoading = true;
// });
// await _userService.updateLanguage(localeCode);
// fetchNotifications(localeCode);
// });
ref.listen<Locale?>(localeProvider, (previous, next) async {
final localeCode = next?.languageCode ?? 'en';
setState(() {
isLoading = true;
});
await _userService.updateLanguage(localeCode, ref);
final currentTheme = ref.read(themeProvider);
final isDark = currentTheme == ThemeMode.dark ||
(currentTheme == ThemeMode.system &&
MediaQuery.of(context).platformBrightness == Brightness.dark);
final themeString = isDark ? 'dark' : 'light';
fetchData(localeCode, themeString);
await fetchNotifications(localeCode);
_processNotifications(notifications, userReadedNotiOrNot);
});
ref.listen<ThemeMode>(themeProvider, (previous, next) async {
final localeCode = ref.watch(localeProvider)?.languageCode ?? 'en';
// Default to 'en' if null
setState(() {
isLoading = true;
});
final currentTheme = ref.read(themeProvider);
final isDark = currentTheme == ThemeMode.dark ||
(currentTheme == ThemeMode.system &&
MediaQuery.of(context).platformBrightness == Brightness.dark);
final themeString = isDark ? 'dark' : 'light';
fetchData(localeCode, themeString);
await fetchNotifications(localeCode);
_processNotifications(notifications, userReadedNotiOrNot);
});
final themeMode = ref.read(themeProvider);
final isDarkTheme = themeMode == ThemeMode.dark ||
(themeMode == ThemeMode.system &&
MediaQuery.of(context).platformBrightness == Brightness.dark);
final List<Map<String, dynamic>> tabs = [
{'title': AppLocalizations.of(context)!.all_updates},
{'title': AppLocalizations.of(context)!.app_updates},
{'title': AppLocalizations.of(context)!.notificationSocial},
{'title': AppLocalizations.of(context)!.notificationEconomy},
{'title': AppLocalizations.of(context)!.notificationEnvironment},
{'title': AppLocalizations.of(context)!.notificationFavourites},
];
// final List<Map<String, dynamic>> tabs = [
// {
// 'title': AppLocalizations.of(context)!.all_updates,
// 'color': Colors.blue,
// },
// {
// 'title': AppLocalizations.of(context)!.app_updates,
// 'color': Colors.green,
// },
// {
// 'title': AppLocalizations.of(context)!.notificationSocial,
// 'color': Color(0xFF675863),
// },
// {
// 'title': AppLocalizations.of(context)!.notificationEconomy,
// 'color': Color(0xFF89AFD8),
// },
// {
// 'title': AppLocalizations.of(context)!.notificationEnvironment,
// 'color': Color(0xFFB3CF99),
// },
// {
// 'title': AppLocalizations.of(context)!.notificationFavourites,
// 'color': Colors.red,
// },
// ];
// Filter notifications based on the selected tab
List<Map<String, dynamic>> filteredNotifications = selectedTabIndex == 0
? notifications
: notifications.where((notification) {
// print("Comparing: '${notification["category"]}' with '${tabs[selectedTabIndex]}'");
return notification["category"] == tabs[selectedTabIndex]['title'];
}).toList();
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
context.go('/myhomepage');
},
child: BaseScaffold(
backgroundColor: isDarkTheme ? Colors.black : Colors.white,
appbarColor: isDarkTheme ? Colors.black : Colors.white,
title: Text(
context.translate(
'Notification $notificationCount',
'$notificationCount إشعار ',
),
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
)),
),
body: isLoading
? Container(
// color: Color(0x98FFFCE5), // Semi-transparent background
color: isDarkTheme
? Colors.black
: Color(0x98FFFCE5), // Semi-transparent background
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
margin: EdgeInsets.symmetric(
horizontal: 40), // Left & Right space
child: LinearProgressIndicator(
minHeight: 5, // Adjust thickness
backgroundColor:
Colors.grey[100], // Optional: Background color
valueColor: AlwaysStoppedAnimation<Color>(
Color(0xFFAA8E83)), // Loader color
),
),
],
),
)
: Column(
children: [
TabBarHeader(
tabs: tabs.map((tab) => tab['title'] as String).toList(),
selectedIndex: selectedTabIndex,
isDarkTheme: isDarkTheme,
onTabSelected: (index) {
setState(() {
selectedTabIndex = index;
});
},
),
Expanded(
child: filteredNotifications.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.info_outline,
size: 100, color: Colors.grey[400]),
SizedBox(height: 16),
Text(
'No notifications available.',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
),
),
],
),
)
: ListView.builder(
itemCount: filteredNotifications.length,
itemBuilder: (context, index) {
final notification = filteredNotifications[index];
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xFFDEDEDE),
width: 1), // Bottom border only
),
),
child: NotificationTile(
title: notification['title'] ?? 'No Title',
date:
formatDate(notification['created'] ?? ''),
category:
notification['category'] ?? 'Unknown',
message: notification['message'] ?? '',
id: notification['id'],
userReadedNotiOrNot: userReadedNotiOrNot,
userID: userID,
isDarkTheme: isDarkTheme,
),
);
},
),
),
],
),
),
);
}
}
class TabBarHeader extends StatelessWidget {
final List<String> tabs;
final int selectedIndex;
final ValueChanged<int> onTabSelected;
bool isDarkTheme;
TabBarHeader({
required this.tabs,
required this.selectedIndex,
required this.onTabSelected,
required this.isDarkTheme,
});
@override
Widget build(BuildContext context) {
return Container(
color: isDarkTheme ? Colors.black : Colors.white,
padding: const EdgeInsets.symmetric(vertical: 8),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: tabs.asMap().entries.map((entry) {
int index = entry.key;
String text = entry.value;
return Row(
children: [
TextButton(
onPressed: () => onTabSelected(index),
style: ButtonStyle(
foregroundColor:
MaterialStateProperty.resolveWith((states) {
return selectedIndex == index
? isDarkTheme
? Color(0xFFFFFFFF)
: Color(0xFFB68A34)
: isDarkTheme
? Color(0xFFBBBBBB)
: Color(0xFF898C81);
}),
),
child: Text(
text,
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
),
),
),
if (index < tabs.length - 1) buildDivider(),
],
);
}).toList(),
),
),
);
}
Widget buildDivider() {
return Container(
height: 20,
child: const VerticalDivider(
color: Colors.grey,
thickness: 1,
width: 16,
),
);
}
}
class NotificationTile extends ConsumerWidget {
final String title;
final String message;
final String date;
final String category;
final String id;
final String userID;
final List<String> userReadedNotiOrNot;
bool isDarkTheme;
NotificationTile({
required this.title,
required this.message,
required this.date,
required this.category,
required this.id,
required this.userID,
required this.userReadedNotiOrNot,
required this.isDarkTheme,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
bool isPushed = userReadedNotiOrNot.contains(id);
return GestureDetector(
onTap: () {
readedNotification(userID, id, ref);
final encodedKey = Uri.encodeQueryComponent('notification');
context.go('/notification_details', extra: {
'title': title,
'message': message,
'date': date,
'category': category,
'id': id,
'backNavigation': encodedKey
});
},
child: ListTile(
// leading: isPushed
// ? const Icon(Icons.circle, size: 12, color: Color(0xFFFF274E))
// : Icon(Icons.circle,
// size: 12,
// color: isDarkTheme ? Colors.white : const Color(0x99414042)),
// leading: isPushed
// ? const Icon(Icons.circle, size: 12, color: Color(0xFFFF274E))
// : const Icon(Icons.circle, size: 12, color: Colors.transparent),
title: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: 4.0, right: 6),
child: isPushed
? const Icon(Icons.circle, size: 8, color: Color(0xFFFF274E))
: const Icon(Icons.circle,
size: 8, color: Colors.transparent),
),
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
fontFamily: context.translate('Roboto', 'NotoKufi'),
color: isPushed
? (isDarkTheme
? Colors.white
: Color(0xFF414042)) // dark title
: (isDarkTheme
? Color(0xFF999999)
: Color(0x99414042)), // light grey title
),
),
),
],
),
// Text(
// title,
// style: TextStyle(
// color: isPushed
// ? isDarkTheme
// ? Colors.white
// : Color(0xFF414042)
// : isDarkTheme ? Color(0xFF999999) : Color(0xFF999999),
// fontWeight: FontWeight.bold,
// fontFamily: context.translate(
// 'Roboto',
// 'NotoKufi',
// ),
// ),
// ),
subtitle: Padding(
padding: const EdgeInsets.only(
left: 15), // 👈 set your desired left padding here
child: Text(
date,
style: TextStyle(
color: isDarkTheme ? Color(0xFF565656) : Color(0xFF8E8E8E),
fontSize: 12,
fontWeight: FontWeight.w400,
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
),
),
),
trailing: const Icon(Icons.arrow_forward_ios,
size: 16, color: Color(0xFF898C81)),
),
);
}
}
Future<void> readedNotification(
String userId, String notificationId, WidgetRef ref) async {
final prefs = await SharedPreferences.getInstance();
final read = prefs.getStringList('read_notification_ids') ?? [];
if (!read.contains(notificationId)) {
read.add(notificationId);
await prefs.setStringList('read_notification_ids', read);
}
}