notification bug fix

This commit is contained in:
venbaittech 2025-03-06 18:51:53 +05:30
parent b5d0b38ac7
commit 44c21defa7
8 changed files with 529 additions and 307 deletions

View File

@ -22,7 +22,7 @@ class LoadingOverlay {
color: Colors.black.withOpacity(0.5), dismissible: false),
Center(
child: Image.asset(
"assets/images/loading.gif", // Make sure to add the GIF in assets
"assets/FCSC-GIF.gif", // Make sure to add the GIF in assets
width: 100,
height: 100,
),

View File

@ -530,6 +530,8 @@ final GoRouter router = GoRouter(
message: data['message'] ?? '',
date: data['date'] ?? '',
category: data['category'] ?? '',
id: data['id'] ?? '',
backNavigation: data['backNavigation'] ?? '',
);
},
),
@ -650,16 +652,17 @@ final GoRouter router = GoRouter(
),
],
redirect: (context, state) async {
if (state.uri.path == '/SessionCheckScreen' || state.uri.path == '/login') {
if (state.uri.path == '/SessionCheckScreen' ||
state.uri.path == '/login') {
return null;
}
final session = await PAuthRepo().fetchLocallyStored();
if (session == null || session.freshness == SessionFreshness.expired) {
print('Session expired, redirecting to login with intendedPath: ${state.uri.path}');
print(
'Session expired, redirecting to login with intendedPath: ${state.uri.path}');
return '/login${state.uri.path != '/' ? '?intendedPath=${state.uri.path}' : ''}';
}
return null;
}
);
});

View File

@ -45,6 +45,13 @@
"account_confirmation": "هل لديك حساب؟ سجل الدخول",
"login_title": "تسجيل الدخول",
"all_updates": "جميع التحديثات",
"app_updates": "تحديثات التطبيق",
"notificationSocial": "اجتماعي",
"notificationEconomy": "اقتصاد",
"notificationEnvironment": "بيئة",
"notificationFavourites": "المفضلة",
"profile_title": "نموذج الملاحظات",
"my_profile": "ملفي الشخصي",
"logout": "تسجيل الخروج",

View File

@ -1,8 +1,8 @@
{
"all_bookmarks":"All Bookmarks",
"economy_title":"Economy",
"social_title":"Social",
"environment_title":"Environment",
"all_bookmarks": "All Bookmarks",
"economy_title": "Economy",
"social_title": "Social",
"environment_title": "Environment",
"bookmarks": "Bookmarks",
"feedback_title": "Feedback",
@ -41,7 +41,12 @@
"account_confirmation": "Already have an account?",
"login_title": "Login",
"all_updates": "All updates",
"app_updates": "App updates",
"notificationSocial": "Social",
"notificationEconomy": "Economy",
"notificationEnvironment": "Environment",
"notificationFavourites": "Favourites",
"profile_title": "Edit Profile",
"my_profile": "My Profile",

View File

@ -65,7 +65,19 @@ class _EditProfileState extends State<EditProfile> {
'India',
'Canada',
];
final List<String> preferred_language = ['English', 'Arabic'];
final Map<String, String> languageMap = {
"en": "English",
"ar": "Arabic",
};
// Reverse mapping for saving back to API
final Map<String, String> reverseLanguageMap = {
"English": "en",
"Arabic": "ar",
};
String? _selectedCountry;
String? _selectedLanguage;
bool isChecked = false;
bool showError = false;
final _picker = ImagePicker();
@ -82,6 +94,7 @@ class _EditProfileState extends State<EditProfile> {
dynamic role;
bool _isHoveringDropdown = false;
bool _isHoveringPreferredLang = false;
@override
void initState() {
@ -177,10 +190,16 @@ class _EditProfileState extends State<EditProfile> {
print('userDetails: $userDetailsResponse');
setState(() {
setState(() {
_selectedLanguage =
languageMap[userDetailsResponse.data['language']] ??
'English'; // Default to English if not found
});
_usernameController.text = userDetailsResponse.data['uname'] ?? '';
_emailController.text = userDetailsResponse.data['email'] ?? '';
_fullNameController.text = userDetailsResponse.data['full_name'] ?? '';
_selectedCountry = userDetailsResponse.data['country_region'] ?? '';
// _selectedLanguage = userDetailsResponse.data['language'] ?? '';
_isProfileCompleted =
userDetailsResponse.data['is_profile_completed'] ?? false;
//_isProfileCompleted = true ;
@ -321,6 +340,13 @@ class _EditProfileState extends State<EditProfile> {
return null;
}
String? _validatePreferredLang(String? value) {
if (value == null || value.isEmpty) {
return context.translate('Required', 'مطلوب');
}
return null;
}
void _toggleCheckbox(bool? value) {
setState(() {
isChecked = value ?? false;
@ -335,7 +361,8 @@ class _EditProfileState extends State<EditProfile> {
// if (result == true) {
// Validate only the country field
if (_validateDropdown(_selectedCountry) == null) {
if (_validateDropdown(_selectedCountry) == null &&
_validatePreferredLang(_selectedLanguage) == null) {
try {
String userID = userId;
@ -343,6 +370,7 @@ class _EditProfileState extends State<EditProfile> {
// Retrieve data from the country/region field
String countryRegion =
_selectedCountry ?? ''; // Ensure the country is selected
String preferredLang = _selectedLanguage ?? '';
// Create a multipart request
final uri =
@ -352,6 +380,7 @@ class _EditProfileState extends State<EditProfile> {
// Add fields to the request
request.fields['country_region'] =
countryRegion; // Only update country here
request.fields['language'] = preferredLang == 'English' ? 'en' : 'ar';
// If profile image exists, add it
if (_profileImage != null) {
@ -406,6 +435,7 @@ class _EditProfileState extends State<EditProfile> {
_fullNameController.clear();
_dateController.clear();
_selectedCountry = null;
_selectedLanguage = null;
isChecked = false;
// Reset profile image
@ -829,6 +859,73 @@ class _EditProfileState extends State<EditProfile> {
validator: _validateDropdown,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
AppLocalizations.of(context)!
.preferredLang,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500),
),
],
),
SizedBox(height: 10),
MouseRegion(
onEnter: (_) => setState(
() => _isHoveringPreferredLang = true),
onExit: (_) => setState(
() => _isHoveringPreferredLang = false),
child: DropdownButtonFormField<String>(
style: TextStyle(
fontFamily: 'Roboto',
fontSize: 16,
fontWeight: FontWeight.normal,
color: Color(0xFF544C4C),
),
value: _selectedLanguage,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: BorderSide(
color: MyTheme.topicColor(
IndicatorTopic.economy)
.shade400,
width: 1), // Enabled border
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFF7296BE),
),
borderRadius:
BorderRadius.circular(8),
),
labelText: 'Select',
),
icon: Icon(
Icons.keyboard_arrow_down_sharp,
color: _isHoveringPreferredLang
? Colors.black
: Colors.grey),
items: preferred_language
.map(
(item) => DropdownMenuItem<String>(
value: item,
child: Text(item),
),
)
.toList(),
onChanged: (String? newValue) {
setState(() {
_selectedLanguage = newValue;
});
},
validator: _validatePreferredLang,
),
),
SizedBox(height: 20),
if (!_isProfileCompleted) // Conditional rendering
Column(

View File

@ -10,6 +10,7 @@ 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:flutter_gen/gen_l10n/app_localizations.dart';
class NotificationPage extends ConsumerStatefulWidget {
@override
@ -66,6 +67,7 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
setState(() {
notifications = List<Map<String, dynamic>>.from(
jsonResponse['data']); // Assign decoded data
print('notifications $notifications');
});
} else {
throw Exception('Failed to load data');
@ -100,13 +102,21 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
fetchNotifications(localeCode);
});
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},
];
// 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];
// print("Comparing: '${notification["category"]}' with '${tabs[selectedTabIndex]}'");
return notification["category"] == tabs[selectedTabIndex]['title'];
}).toList();
return PopScope(
@ -125,7 +135,7 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
body: Column(
children: [
TabBarHeader(
tabs: tabs,
tabs: tabs.map((tab) => tab['title'] as String).toList(),
selectedIndex: selectedTabIndex,
onTabSelected: (index) {
setState(() {
@ -165,7 +175,9 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
title: notification['title'] ?? 'No Title',
date: formatDate(notification['created'] ?? ''),
category: notification['category'] ?? 'Unknown',
message: notification['message'] ?? ''),
message: notification['message'] ?? '',
id: notification['id'],
),
);
},
),
@ -240,23 +252,28 @@ class NotificationTile extends StatelessWidget {
final String message;
final String date;
final String category;
final String id;
const NotificationTile({
required this.title,
required this.message,
required this.date,
required this.category,
required this.id,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
final encodedKey = Uri.encodeQueryComponent('notification');
context.go('/notification_details', extra: {
'title': title,
'message': message,
'date': date,
'category': category,
'id': id,
'backNavigation': encodedKey
});
},
child: ListTile(

View File

@ -1,14 +1,22 @@
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:uae_stat/config/api_config.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import '../../custom_drawer_routes.dart';
import 'package:http/http.dart' as http;
class NotificationDetails extends ConsumerWidget {
class NotificationDetails extends ConsumerStatefulWidget {
final String title;
final String message;
final String date;
final String category;
final String id;
final String backNavigation;
const NotificationDetails({
super.key,
@ -16,10 +24,85 @@ class NotificationDetails extends ConsumerWidget {
required this.message,
required this.date,
required this.category,
required this.id,
required this.backNavigation,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
_NotificationDetailsState createState() => _NotificationDetailsState();
}
class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
// Example notification data
final _pb = PocketBase(apiUrl);
Map<String, dynamic>? notification;
// Current selected tab index
int selectedTabIndex = 0;
late final Locale locale;
dynamic notificationID;
@override
void initState() {
super.initState();
print(widget.id);
locale = ref.read(localeProvider) ?? const Locale('en');
fetchNotifications(locale?.languageCode ?? 'en');
}
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 {
notificationID = widget.id;
final baseUrl = apiUrl + '/api/getNotification';
try {
final response = await http
.get(Uri.parse('$baseUrl?language=$locale&id=$notificationID'));
if (response.statusCode == 200) {
final Map<String, dynamic> jsonResponse =
jsonDecode(response.body); // Decode JSON
if (jsonResponse['message'] == 'Success') {
setState(() {
final data = jsonResponse['data'];
notification = data;
// if (data is Map<String, dynamic>) {
// notification = data;
// } else {
// notification = null; // Default to null if data is not valid
// }
print('notification $notification');
});
} else {
throw Exception('Failed to load data');
}
} else {
throw Exception(
'Failed to load data with status code ${response.statusCode}');
}
} catch (e) {
print('Error fetching data: $e');
}
}
@override
Widget build(BuildContext context) {
ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
fetchNotifications(localeCode);
});
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
@ -34,13 +117,14 @@ class NotificationDetails extends ConsumerWidget {
),
),
showBackButton: true,
navBackArrow: Text(widget.backNavigation ?? 'Default Value'),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
notification?['title'] ?? '',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
@ -52,7 +136,7 @@ class NotificationDetails extends ConsumerWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
category == 'App updates'
notification?['category'] == 'App updates'
? 'assets/backgrounds/Notification/App-Update.png'
: 'assets/backgrounds/Notification/Update_notific.png',
height: 200,
@ -62,9 +146,8 @@ class NotificationDetails extends ConsumerWidget {
),
const SizedBox(height: 20),
Text(
message.isNotEmpty
? message
: "No additional details available.",
notification?['message'] ??
"No additional details available.",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
@ -72,11 +155,12 @@ class NotificationDetails extends ConsumerWidget {
),
const SizedBox(height: 10),
Text(
"Date: $date",
"Date: ${notification != null ? formatDate(notification!['created'] ?? '') : 'N/A'}",
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Color(0xFF8E8E8E)),
color: Color(0xFF8E8E8E),
),
),
],
),

View File

@ -67,6 +67,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
String? userEmail;
String? userAvatar;
String? role;
String? preferredLanguage;
String? userNames;
String? userEmails;
String? userAvatars;
@ -654,17 +655,23 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
userEmail = userDetailsResponse.data['email'] ?? '';
userAvatar = userDetailsResponse.data['avatar'] ?? '';
role = userDetailsResponse.data['role'] ?? '';
preferredLanguage = userDetailsResponse.data['language'] ?? '';
String recordId = userId;
String collectionId =
userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
if (userAvatar!.isNotEmpty && recordId.isNotEmpty) {
_avatarUrl =
'$apiUrl/api/files/$collectionId/$recordId/$userAvatar';
_avatarUrl = '$apiUrl/api/files/$collectionId/$recordId/$userAvatar';
} else {
_avatarUrl = ''; // Reset to default or empty
}
});
// **Set Default Locale in Provider**
if (preferredLanguage == 'ar') {
ref.read(localeProvider.notifier).setLocale(const Locale('ar'));
} else {
ref.read(localeProvider.notifier).setLocale(const Locale('en'));
}
} catch (e) {
print('Error fetching user details: $e');
}
@ -706,7 +713,9 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
title: Padding(
padding: EdgeInsets.only(top: 10),
child: Align(
alignment: (locale?.languageCode == 'ar') ? Alignment.centerRight : Alignment.centerLeft,
alignment: (locale?.languageCode == 'ar')
? Alignment.centerRight
: Alignment.centerLeft,
child: DefaultTextStyle(
style: TextStyle(
color: widget.colorChange ? Colors.white : Color(0xFF414042),
@ -775,12 +784,12 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
color: widget.colorChange ? Colors.white : Colors.black,
icon: const Icon(Icons.arrow_back_ios_new, size: 24),
onPressed: () {
// if ((widget.navBackArrow as Text).data == 'home') {
// context.go('/myhomepage');
// } else {
// context.go('/uaenumbers');
// }
if ((widget.navBackArrow as Text).data ==
'notification') {
context.go('/notification');
} else {
context.pop();
}
},
),
),
@ -1195,7 +1204,7 @@ void _showLogoutConfirmationDialog(BuildContext context) {
),
actions: [
Row(
mainAxisAlignment:MainAxisAlignment.spaceEvenly,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(