upgrade mapbox

This commit is contained in:
2025-08-29 23:24:27 +02:00
parent 6e0b54a925
commit b6134c5506
163 changed files with 15961 additions and 0 deletions

View File

@@ -0,0 +1,209 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import 'dart:io';
import '../pages/EditProfile.dart';
import '../pages/EditSettings.dart';
import '../pages/ListItemMenu.dart';
import 'alert.dart';
import '../variable/globals.dart' as globals;
import '../pages/LoginDemo.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv
import 'package:encrypt_shared_preferences/provider.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class MyDrawer extends StatelessWidget with ShowAlertDialog {
Future<void> logout(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
if (accessToken.isNotEmpty) {
var url = Uri.parse("${globals.api}/token");
try {
var response = await http.delete(url, headers: {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=${accessToken}"
});
print("Status code logout ${response.statusCode}");
if (response.statusCode == 200) {
await prefs.remove("access_token");
await dotenv.load(fileName: ".env"); // Load .env file
final keyEncrypt = dotenv.env['KEY_ENCRYPT'] ?? '';
if (keyEncrypt.isNotEmpty) {
await EncryptedSharedPreferences.initialize(keyEncrypt);
var sharedPref = EncryptedSharedPreferences.getInstance();
String username = sharedPref.getString("username") ?? "";
String password = sharedPref.getString("password") ?? "";
if ((username.isEmpty) || (password.isEmpty)) {
sharedPref.remove("username");
sharedPref.remove("password");
}
}
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => LoginDemo()),
(route) => false, // Remove all previous routes
);
} else {
String errorMessage;
switch (response.statusCode) {
case 400:
errorMessage = "Bad Request: Please check your input.";
break;
case 401:
errorMessage = "Unauthorized: Invalid credentials.";
break;
case 403:
errorMessage = "Forbidden: You don't have permission.";
break;
case 404:
errorMessage = "Not Found: The resource was not found.";
break;
case 500:
errorMessage =
"Server Error: Something went wrong on the server.";
break;
default:
errorMessage = "Unexpected Error: ${response.statusCode}";
break;
}
print(errorMessage);
showAlertDialog(context, "Error", errorMessage);
}
} catch (e) {
print("Error: $e");
showAlertDialog(
context, "Error", "An error occurred. Please try again.");
}
} else {
showAlertDialog(context, "Error", "Invalid token.");
}
}
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
final localeProvider = Provider.of<LocaleProvider>(context);
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
// Drawer Header
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
'Menu',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
// Drawer Items
ListTile(
leading: Icon(Icons.home),
title: Text(loc?.home ?? "Home"),
onTap: () {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
/// Close the drawer
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text(loc?.settings ?? 'Settings'),
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => EditSettings())); // Close the drawer
},
),
ListTile(
leading: Icon(Icons.account_circle),
title: Text(loc?.update_profile ?? 'Update profile'),
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => EditProfile())); // Close the drawer
},
),
ListTile(
leading: Icon(Icons.language),
title: Text(loc?.language ?? 'Language'),
onTap: () {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text(loc?.select_language ?? 'Select Language'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: Icon(Icons.flag),
title: Text(loc?.french ?? 'Français'),
onTap: () {
Provider.of<LocaleProvider>(context, listen: false)
.setLocale(const Locale('fr'));
Navigator.of(context).pop();
},
),
ListTile(
leading: Icon(Icons.flag_outlined),
title: Text(loc?.english ?? 'English'),
onTap: () {
Provider.of<LocaleProvider>(context, listen: false)
.setLocale(const Locale('en'));
Navigator.of(context).pop();
},
),
ListTile(
leading: Icon(Icons.flag_outlined),
title: Text(loc?.german ?? 'German'),
onTap: () {
Provider.of<LocaleProvider>(context, listen: false)
.setLocale(const Locale('de'));
Navigator.of(context).pop();
},
),
],
),
),
);
},
),
ListTile(
leading: Icon(Icons.info),
title: Text(loc?.about ?? 'About'),
onTap: () {
showAlertDialog(context, loc?.about ?? 'About',
"Version 0.0.1"); // Close the drawer
},
),
ListTile(
leading: Icon(Icons.logout),
title: Text(loc?.log_out ?? 'Log out'),
onTap: () async {
logout(context);
// Close the drawer
},
),
],
),
);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
class AdHelper {
static Future<BannerAd> createBannerAd(Function setStateCallback) async {
await dotenv.load(fileName: ".env");
final adUnitId = dotenv.env['AD_UNIT_ID'] ?? '';
BannerAd bannerAd = BannerAd(
adUnitId: adUnitId,
size: AdSize.banner,
request: AdRequest(),
listener: BannerAdListener(
onAdLoaded: (ad) {
setStateCallback(() {});
},
onAdFailedToLoad: (ad, error) {
print('Banner Ad failed to load: $error');
ad.dispose();
},
),
);
bannerAd.load();
return bannerAd;
}
}

View File

@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'events.dart';
import '../variable/globals.dart' as globals;
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
mixin ShowDescImageAdd<T extends StatefulWidget> on State<T> {
Future<void> addEvents(var events) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
List<String> send = ["toto"];
if (accessToken.isNotEmpty) {
var urlPut = Uri.parse("${globals.api}/events");
print("start date : ${events["start_date"]}");
var responsePut = await http.put(urlPut,
headers: {
HttpHeaders.cookieHeader: 'access_token=${accessToken}',
HttpHeaders.acceptHeader: 'application/json, text/plain, */*',
HttpHeaders.contentTypeHeader: 'application/json'
},
body: jsonEncode({
'name': events["name"],
'place': events["place"],
'start_date': events['date'],
'end_date': events['date'],
'organizers': send,
'latitude': '0.0',
'longitude': '0.0',
}));
print("http put code status : ${responsePut.statusCode}");
print("http put body : ${responsePut.body}");
}
}
void showDescImageAddDialog(BuildContext context, var events) {
// Create AlertDialog
String name = events['name'];
AlertDialog dialog = AlertDialog(
title: Text("Ajouter un evenement"),
content: Text("${name} n'a pas été trouvé. Voulez-vous l'ajouter ? "),
actions: [
TextButton(
child: Text("Annuler"),
onPressed: () {
Navigator.of(context).pop("Yes, Of course!"); // Return value
}),
TextButton(
child: Text("Oui"),
onPressed: () {
addEvents(events); // Return value
}),
],
);
// Call showDialog function to show dialog.
Future futureValue = showDialog(
context: context,
builder: (BuildContext context) {
return dialog;
});
}
}

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
mixin ShowAlertDialog {
void showAlertDialog(BuildContext context, String title, String text) {
// Create AlertDialog
AlertDialog dialog = AlertDialog(
title: Text(title),
content: Text(text),
actions: [
ElevatedButton(
child: Text("OK"),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 50, vertical: 20),
textStyle:
TextStyle(fontSize: 15, fontWeight: FontWeight.normal)),
onPressed: () {
Navigator.of(context).pop("Yes, Of course!"); // Return value
}),
],
);
// Call showDialog function to show dialog.
Future futureValue = showDialog(
context: context,
builder: (BuildContext context) {
return dialog;
});
}
}

View File

@@ -0,0 +1,141 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import '../variable/globals.dart' as globals;
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:encrypt_shared_preferences/provider.dart';
import '../pages/LoginDemo.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv
class AuthService {
// Login with username and password
Future<bool> login(String username, String password,
{bool rememberMe = false}) async {
final url = Uri.parse("${globals.api}/token");
try {
final response = await http.post(
url,
headers: {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {"username": username, "password": password},
);
if (response.statusCode == 200 || response.statusCode == 201) {
final prefs = await SharedPreferences.getInstance();
if (rememberMe) {
await dotenv.load(fileName: ".env"); // Load .env file
final keyEncrypt = dotenv.env['KEY_ENCRYPT'] ?? '';
if (keyEncrypt.isNotEmpty) {
await EncryptedSharedPreferences.initialize(keyEncrypt);
var sharedPref = EncryptedSharedPreferences.getInstance();
sharedPref.setString("username", username);
sharedPref.setString("password", password);
}
}
final cookies = response.headers["set-cookie"]?.split(";") ?? [];
for (final cookie in cookies) {
final cookieParts = cookie.split(",");
for (final part in cookieParts) {
final keyValue = part.split("=");
if (keyValue.length == 2 && keyValue[0] == "access_token") {
prefs.setString("access_token", keyValue[1]);
}
}
}
return true;
} else {
return false;
}
} catch (e) {
print("Login error: $e");
return false;
}
}
// Logout
Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove("access_token");
}
Future<bool> isLoggedIn() async {
final prefs = await SharedPreferences.getInstance();
final accessToken = prefs.getString("access_token");
if (accessToken == null || accessToken.isEmpty) {
print("No access token found.");
return false;
}
print("Checking token validity...");
var url = Uri.parse("${globals.api}/token");
try {
final response = await http.get(
url,
headers: {
HttpHeaders.cookieHeader: "access_token=$accessToken",
"Content-Type": "application/json",
},
);
if (response.statusCode == 200) {
print("Token is valid.");
return true;
} else {
print("Token is invalid. Status code: ${response.statusCode}");
await dotenv.load(fileName: ".env"); // Load .env file
final keyEncrypt = dotenv.env['KEY_ENCRYPT'] ?? '';
if (keyEncrypt.isNotEmpty) {
await EncryptedSharedPreferences.initialize(keyEncrypt);
var sharedPref = EncryptedSharedPreferences.getInstance();
String username = sharedPref.getString("username") ?? "";
String password = sharedPref.getString("password") ?? "";
if ((username.isEmpty) || (password.isEmpty)) {
sharedPref.remove("username");
sharedPref.remove("password");
await prefs.remove("access_token"); // Clear invalid token
return false;
} else {
return login(username, password);
}
} else {
return false;
}
}
} catch (e) {
print("Error while checking token: $e");
return false;
}
}
Future<void> checkTokenStatus(context) async {
bool loggedIn = await isLoggedIn();
SharedPreferences prefs = await SharedPreferences.getInstance();
if (!loggedIn) {
await prefs.remove("access_token"); // Correctly remove the token
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => LoginDemo()),
(route) => false, // Remove all previous routes
);
}
}
// Get stored access token
Future<String?> getAccessToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString("access_token");
}
// Login with Google
}

View File

@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import '../pages/ListItemMenu.dart';
mixin ShowEventDialog<T extends StatefulWidget> on State<T> {
void showEventDialog(BuildContext context, String text) {
// Create AlertDialog
AlertDialog dialog = AlertDialog(
title: Text("Evenement ajoute"),
content: Text(text),
actions: [
ElevatedButton(
child: Text("OK"),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 50, vertical: 20),
textStyle:
TextStyle(fontSize: 15, fontWeight: FontWeight.normal)),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ListItemMenu())); // Return value
}),
],
);
// Call showDialog function to show dialog.
Future futureValue = showDialog(
context: context,
builder: (BuildContext context) {
return dialog;
});
}
}

View File

@@ -0,0 +1,54 @@
class Events {
String? id;
String? name;
String? place;
String? startDate;
String? endDate;
String? description;
String? link;
String? ticket;
double? latitude;
double? longitude;
List<String>? tags;
List<String>? organizers;
String? imgUrl;
int? interestedCount;
bool? interested;
Events(
{this.place,
this.id,
this.name,
this.startDate,
this.description,
this.endDate,
this.tags,
this.latitude,
this.longitude,
this.organizers,
this.link,
this.ticket,
this.imgUrl,
this.interestedCount,
this.interested});
Events.fromJson(Map<String, dynamic> json) {
id = json['id'] as String?;
name = json['name'] as String?;
place = json['place'] as String?;
startDate = json['start_date'] as String?;
endDate = json['end_date'] as String?;
description = json['description'] as String?;
latitude = (json['latitude'] as num?)?.toDouble(); // Safely cast to double
longitude =
(json['longitude'] as num?)?.toDouble(); // Safely cast to double
tags = (json['tags'] as List<dynamic>?)
?.cast<String>(); // Convert List<dynamic> to List<String>
organizers = (json['organizers'] as List<dynamic>?)
?.cast<String>(); // Convert List<dynamic> to List<String>
imgUrl = json['imgUrl'] as String?;
link = json['link'] as String?;
ticket = json['ticket'] as String?;
interested = json['interested'] as bool?;
interestedCount = json['interested_count'] as int?;
}
}

View File

@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'events.dart';
import '../variable/globals.dart' as globals;
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
mixin ShowDescImageGet<T extends StatefulWidget> on State<T> {
Future<void> getEvents(var events) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
List<String> send = ["toto"];
if (accessToken.isNotEmpty) {
var urlPut = Uri.parse("${globals.api}/events");
print("start date : ${events["start_date"]}");
var responsePut = await http.put(urlPut,
headers: {
HttpHeaders.cookieHeader: 'access_token=${accessToken}',
HttpHeaders.acceptHeader: 'application/json, text/plain, */*',
HttpHeaders.contentTypeHeader: 'application/json'
},
body: jsonEncode({
'name': events["name"],
'place': events["place"],
'start_date': events['date'],
'end_date': events['date'],
'organizers': send,
'latitude': '0.0',
'longitude': '0.0',
}));
print("http put code status : ${responsePut.statusCode}");
print("http put body : ${responsePut.body}");
}
}
void showDescImageGetDialog(BuildContext context, var events) {
// Create AlertDialog
String name = events['name'];
AlertDialog dialog = AlertDialog(
title: Text("Voir la description de l'evenement"),
content: Text("${name} a été trouvé. Voulez-vous voir sa description ? "),
actions: [
TextButton(
child: Text("Annuler"),
onPressed: () {
Navigator.of(context).pop("Yes, Of course!"); // Return value
}),
TextButton(
child: Text("Oui"),
onPressed: () {
getEvents(events); // Return value
}),
],
);
// Call showDialog function to show dialog.
Future futureValue = showDialog(
context: context,
builder: (BuildContext context) {
return dialog;
});
}
}

View File

@@ -0,0 +1,91 @@
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
class NotificationService {
static final FlutterLocalNotificationsPlugin _notificationsPlugin =
FlutterLocalNotificationsPlugin();
/// Initialisation (à appeler dans main())
static Future<void> initialize() async {
tz.initializeTimeZones();
const AndroidInitializationSettings androidInitSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');
const InitializationSettings initSettings = InitializationSettings(
android: androidInitSettings,
iOS: DarwinInitializationSettings(),
);
await _notificationsPlugin.initialize(initSettings);
// Demande les permissions au lancement
await requestPermissions();
}
/// Demander les permissions (Android 13+ et iOS)
static Future<void> requestPermissions() async {
// Android 13+
final androidImplementation =
_notificationsPlugin.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
await androidImplementation?.requestNotificationsPermission();
// iOS
final iosImplementation =
_notificationsPlugin.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>();
await iosImplementation?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
/// Planifie une notification 1h avant lévènement
static Future<void> scheduleEventNotification({
required String eventId,
required String title,
required String body,
required DateTime eventDate,
}) async {
final scheduledDate = eventDate.subtract(const Duration(hours: 1));
if (scheduledDate.isBefore(DateTime.now())) {
// Trop tard pour notifier
return;
}
await _notificationsPlugin.zonedSchedule(
eventId.hashCode, // identifiant unique pour lévènement
title,
body,
tz.TZDateTime.from(scheduledDate, tz.local),
const NotificationDetails(
android: AndroidNotificationDetails(
'events_channel',
'Events',
channelDescription: 'Favorite event notifications ',
importance: Importance.high,
priority: Priority.high,
),
iOS: DarwinNotificationDetails(),
),
androidScheduleMode: AndroidScheduleMode
.inexactAllowWhileIdle, // évite l'erreur Exact Alarm
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.dateAndTime,
);
}
/// Annule une notification planifiée
static Future<void> cancel(String eventId) async {
await _notificationsPlugin.cancel(eventId.hashCode);
}
}