Compare commits

..

43 Commits

Author SHA1 Message Date
1c21f59420 Merge pull request 'feature/localeDate' (#45) from feature/localeDate into main
Reviewed-on: #45
2025-06-25 19:16:50 +00:00
7441af6f13 remove if body isnotempty 2025-06-25 21:14:45 +02:00
4f4b0b609c add date locale in list by tags 2025-06-23 23:25:34 +02:00
2e6814c33d add date locale in list by organizers 2025-06-23 23:17:05 +02:00
909b321158 test date format french ok for first page 2025-06-23 23:09:37 +02:00
6641c7938c Merge pull request 'add fetchcount' (#44) from hotfix/fix-counter into main
Reviewed-on: #44
2025-06-23 20:02:46 +00:00
8904032265 add fetchcount 2025-06-18 23:36:24 +02:00
9d35a59ba6 Merge pull request 'hotfix/duplicate-event' (#43) from hotfix/duplicate-event into main
Reviewed-on: #43
2025-06-16 21:05:14 +00:00
303447ff1e check duplicate event 2025-06-16 22:51:01 +02:00
32532246eb add duplicate event check 2025-06-11 23:05:00 +02:00
452faf05d8 Merge pull request 'feature/pagination' (#42) from feature/pagination into main
Reviewed-on: #42
2025-06-02 21:22:13 +00:00
3a64f1ae36 add pagination for other pages 2025-06-02 23:03:49 +02:00
afae3293c4 add current date 2025-06-02 22:22:33 +02:00
600bf8d2f4 add pagination with limit 2025-05-30 23:39:50 +02:00
ec7a286074 pagination wip 2025-05-27 23:36:19 +02:00
6c1c650fa1 add last data 2025-05-25 23:39:37 +02:00
7468c5f24c change fetchCOunt 2025-05-23 23:47:17 +02:00
1bcc8372c6 pagination work for main page 2025-05-23 00:01:48 +02:00
510e366216 add pagination 2025-03-24 22:47:49 +01:00
52580c6568 fetchCount to 0 2025-03-24 22:10:49 +01:00
4f7c8f60d0 add increment and decrement to pagination 2025-03-19 23:40:38 +01:00
ce2a061bf0 Merge pull request 'persist encrypt shared' (#41) from feature/add-encrypted-sharepreferences into main
Reviewed-on: #41
2025-03-12 21:58:27 +00:00
04f4b7ce8e persist encrypt shared 2025-03-12 22:48:35 +01:00
78ea5f0c10 Merge pull request 'add remember me' (#40) from feature/persist-connect into main
Reviewed-on: #40
2025-03-06 22:38:26 +00:00
395d3390cf add remember me 2025-03-06 23:37:29 +01:00
daef20db66 Merge pull request 'feature/check-token' (#39) from feature/check-token into main
Reviewed-on: #39
2025-03-06 20:39:44 +00:00
9df499d198 check token 100% 2025-03-06 21:38:47 +01:00
e7afe8fddb add check token 25% 2025-03-06 21:27:06 +01:00
5c2a58e484 remove package fix 2025-03-06 20:38:15 +01:00
e8bb2f6180 Merge pull request 'feature/oauth' (#38) from feature/oauth into main
Reviewed-on: #38
2025-03-06 19:27:01 +00:00
e4d8648fcc remove pakcage 2025-03-06 20:26:02 +01:00
7f3240242d remove google and facebook auth 2025-03-06 20:21:59 +01:00
c936a02836 re-organisation login info 2025-03-02 16:49:19 +01:00
3352bab860 Merge pull request 'feature/publicite' (#37) from feature/publicite into main
Reviewed-on: #37
2025-02-23 21:20:24 +00:00
c31e275dae add pub 2025-02-23 22:18:01 +01:00
38e9908533 add pub 2025-02-19 23:32:31 +01:00
47c014cdda add pub in update event 2025-02-19 23:24:21 +01:00
729e0ce1ca use many pub in different page 2025-02-19 23:03:12 +01:00
5c2fa27aa7 ad helper 2025-02-18 22:56:50 +01:00
9846c614a1 Merge pull request 'feature/forgotPassword' (#36) from feature/forgotPassword into main
Reviewed-on: #36
2025-02-15 11:26:45 +00:00
d46f268c1b mise en forme 2025-02-15 12:25:03 +01:00
b0477c889c add page password forgot 2025-02-14 23:39:44 +01:00
6fdb5cb2fe Merge pull request 'feature/add-user' (#35) from feature/add-user into main
Reviewed-on: #35
2025-02-14 20:21:05 +00:00
25 changed files with 1281 additions and 493 deletions

View File

@ -6,6 +6,9 @@
android:label="covas_mobile" android:label="covas_mobile"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-4855855675386260~3438207239"/>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

View File

@ -1,3 +1,4 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
flutter.compileSdkVersion=35

View File

@ -47,5 +47,8 @@
<true/> <true/>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string>Your location is needed for showing nearby events</string> <string>Your location is needed for showing nearby events</string>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-4855855675386260~3438207239</string>
</dict> </dict>
</plist> </plist>

View File

@ -9,7 +9,9 @@ import 'alert.dart';
import '../variable/globals.dart' as globals; import '../variable/globals.dart' as globals;
import '../main.dart'; import '../pages/LoginDemo.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv
import 'package:encrypt_shared_preferences/provider.dart';
class MyDrawer extends StatelessWidget with ShowAlertDialog { class MyDrawer extends StatelessWidget with ShowAlertDialog {
Future<void> logout(BuildContext context) async { Future<void> logout(BuildContext context) async {
@ -24,43 +26,54 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog {
"Content-Type": "application/json", "Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=${accessToken}" HttpHeaders.cookieHeader: "access_token=${accessToken}"
}); });
print("Status code logout ${response.statusCode}"); print("Status code logout ${response.statusCode}");
if (response.statusCode == 200) { if (response.statusCode == 200) {
await prefs.setString("access_token", ""); // Clear the token await prefs.remove("access_token");
Navigator.pushReplacement( await dotenv.load(fileName: ".env"); // Load .env file
context, MaterialPageRoute(builder: (_) => LoginDemo()));
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 { } else {
String errorMessage;
switch (response.statusCode) { switch (response.statusCode) {
case 400: case 400:
print("Bad Request: Please check your input."); errorMessage = "Bad Request: Please check your input.";
showAlertDialog(
context, "Bad Request", "Please check your input.");
break; break;
case 401: case 401:
print("Unauthorized: Invalid credentials."); errorMessage = "Unauthorized: Invalid credentials.";
showAlertDialog(context, "Unauthorized", "Invalid credentials.");
break; break;
case 403: case 403:
print("Forbidden: You don't have permission."); errorMessage = "Forbidden: You don't have permission.";
showAlertDialog(context, "Forbidden",
"You don't have permission to access this.");
break; break;
case 404: case 404:
print("Not Found: The resource was not found."); errorMessage = "Not Found: The resource was not found.";
showAlertDialog(
context, "Not Found", "The resource was not found.");
break; break;
case 500: case 500:
print("Server Error: Something went wrong on the server."); errorMessage =
showAlertDialog(context, "Server Error", "Server Error: Something went wrong on the server.";
"Something went wrong on the server.");
break; break;
default: default:
print("Unexpected Error: ${response.statusCode}"); errorMessage = "Unexpected Error: ${response.statusCode}";
showAlertDialog(context, "Error", "Unexpected Error occurred.");
break; break;
} }
print(errorMessage);
showAlertDialog(context, "Error", errorMessage);
} }
} catch (e) { } catch (e) {
print("Error: $e"); print("Error: $e");
@ -68,7 +81,7 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog {
context, "Error", "An error occurred. Please try again."); context, "Error", "An error occurred. Please try again.");
} }
} else { } else {
showAlertDialog(context, "Error", "Token invalide."); showAlertDialog(context, "Error", "Invalid token.");
} }
} }

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,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

@ -1,20 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:http/http.dart' as http; import 'pages/LoginDemo.dart';
import 'dart:convert'; void main() async {
import 'dart:io'; WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
//import 'MyHomePage.dart';
import 'pages/ListItemMenu.dart';
import 'pages/AddProfile.dart';
import 'classes/alert.dart';
import 'variable/globals.dart' as globals;
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(MyApp()); runApp(MyApp());
} }
@ -27,234 +18,3 @@ class MyApp extends StatelessWidget {
); );
} }
} }
class LoginDemo extends StatefulWidget {
@override
_LoginDemoState createState() => _LoginDemoState();
}
class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
TextEditingController inputPseudo = TextEditingController();
TextEditingController inputPassword = TextEditingController();
Future<void> _login(BuildContext context) async {
final url = Uri.parse("${globals.api}/token");
final pseudo = inputPseudo.text;
final password = inputPassword.text;
print("Attempting login");
if (pseudo.isEmpty || password.isEmpty) {
showAlertDialog(context, "Erreur", "Champ vide");
return;
}
print("Request URL: $url");
try {
final response = await http.post(
url,
headers: {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
"username": pseudo,
"password": password,
},
);
print("Response status code: ${response.statusCode}");
if (response.statusCode == 200 || response.statusCode == 201) {
final prefs = await SharedPreferences.getInstance();
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]);
}
}
}
Navigator.push(
context,
MaterialPageRoute(builder: (_) => ListItemMenu()),
);
} else {
_handleErrorResponse(context, response.statusCode);
}
} catch (e) {
showAlertDialog(context, "Erreur", e.toString());
}
}
void _handleErrorResponse(BuildContext context, int statusCode) {
final errorMessages = {
400: "Requête mal construite",
406: "Mot de passe incorrect",
404: "Utilisateur inconnu",
403: "Utilisateur désactivé",
410: "Token invalide",
500: "Problème interne du serveur",
};
final errorMessage =
errorMessages[statusCode] ?? "Problème d'authentification inconnu";
showAlertDialog(context, "Erreur serveur", errorMessage);
}
void start() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var access_token = prefs.getString("access_token") ?? "";
print("Get access token");
if (access_token.isNotEmpty) {
print("Appel HTTP");
var urlToken = Uri.parse("${globals.api}/token");
var responseToken = await http.get(urlToken,
headers: {HttpHeaders.cookieHeader: 'access_token: ${access_token}'});
print(responseToken.statusCode);
if (responseToken.statusCode == 200) {
print("route to item list");
Navigator.push(
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
} else {
prefs.remove("access_token");
}
}
}
@override
void initState() {
_checkLocationPermission();
start();
super.initState();
}
Future<void> _checkLocationPermission() async {
PermissionStatus status = await Permission.location.status;
if (status.isGranted) {
print("Location permission granted");
} else if (status.isDenied) {
print("Location permission denied");
_requestLocationPermission();
} else if (status.isPermanentlyDenied) {
print("Location permission permanently denied");
openAppSettings();
}
}
// Request location permission
Future<void> _requestLocationPermission() async {
PermissionStatus status = await Permission.location.request();
if (status.isGranted) {
print("Location permission granted");
} else if (status.isDenied) {
print("Location permission denied");
} else if (status.isPermanentlyDenied) {
print("Location permission permanently denied");
openAppSettings();
}
}
// Open app settings to allow user to grant permission manually
Future<void> _openAppSettings() async {
bool opened = await openAppSettings();
if (opened) {
print("App settings opened");
} else {
print("Failed to open app settings");
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Login Page"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 60.0),
child: Center(
child: Container(
width: 200,
height: 150,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(50.0)),
child: Image.asset('./images/flutter.png')),
),
),
Padding(
//padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
padding: EdgeInsets.symmetric(horizontal: 15),
child: TextField(
controller: inputPseudo,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Pseudo',
hintText: 'Enter pseudo existent'),
),
),
Padding(
padding: const EdgeInsets.only(
left: 15.0, right: 15.0, top: 15, bottom: 0),
//padding: EdgeInsets.symmetric(horizontal: 15),
child: TextField(
controller: inputPassword,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter secure password'),
),
),
TextButton(
onPressed: () {
//TODO FORGOT PASSWORD SCREEN GOES HERE
},
child: Text(
'Forgot Password',
style: TextStyle(color: Colors.blue, fontSize: 15),
),
),
Container(
height: 50,
width: 250,
decoration: BoxDecoration(
color: Colors.blue, borderRadius: BorderRadius.circular(20)),
child: TextButton(
onPressed: () {
_login(context);
},
child: Text(
'Login',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),
),
SizedBox(
height: 130,
),
InkWell(
child: Text('New User? Create Account'),
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (_) => AddProfile()));
})
],
),
),
);
}
}

View File

@ -5,13 +5,18 @@ import 'package:intl/intl.dart';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import '../main.dart'; import '../pages/LoginDemo.dart';
import '../classes/alert.dart'; import '../classes/alert.dart';
import '../variable/globals.dart' as globals; import '../variable/globals.dart' as globals;
void main() { import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
runApp(MyApp()); runApp(MyApp());
} }
@ -33,6 +38,8 @@ class AddProfile extends StatefulWidget {
} }
class _AddProfileState extends State<AddProfile> with ShowAlertDialog { class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
BannerAd? _bannerAd;
TextEditingController inputUserName = TextEditingController(); TextEditingController inputUserName = TextEditingController();
TextEditingController inputName = TextEditingController(); TextEditingController inputName = TextEditingController();
@ -133,6 +140,12 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
setState(() {
_bannerAd = ad;
});
});
} }
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
@ -154,6 +167,12 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
_bannerAd == null
? SizedBox.shrink()
: SizedBox(
height: _bannerAd!.size.height.toDouble(),
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!)),
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 15.0, right: 15.0, top: 15, bottom: 0), left: 15.0, right: 15.0, top: 15, bottom: 0),

View File

@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import '../classes/MyDrawer.dart'; import '../classes/MyDrawer.dart';
@ -7,6 +6,7 @@ import '../classes/MyDrawer.dart';
import 'DisplayPictureScreen.dart'; import 'DisplayPictureScreen.dart';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../classes/auth_service.dart';
Future<void> main() async { Future<void> main() async {
// Ensure that plugin services are initialized so that `availableCameras()` // Ensure that plugin services are initialized so that `availableCameras()`
@ -46,12 +46,14 @@ class Camera extends StatefulWidget {
class CameraState extends State<Camera> { class CameraState extends State<Camera> {
late CameraController _controller; late CameraController _controller;
late Future<void> _initializeControllerFuture; late Future<void> _initializeControllerFuture;
final AuthService _authService = AuthService();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// To display the current output from the Camera, // To display the current output from the Camera,
// create a CameraController. // create a CameraController.
_authService.checkTokenStatus(context);
_controller = CameraController( _controller = CameraController(
// Get a specific camera from the list of available cameras. // Get a specific camera from the list of available cameras.
@ -95,7 +97,6 @@ class CameraState extends State<Camera> {
// camera preview. Use a FutureBuilder to display a loading spinner until the // camera preview. Use a FutureBuilder to display a loading spinner until the
// controller has finished initializing. // controller has finished initializing.
drawer: MyDrawer(), drawer: MyDrawer(),
body: FutureBuilder<void>( body: FutureBuilder<void>(
future: _initializeControllerFuture, future: _initializeControllerFuture,
builder: (context, snapshot) { builder: (context, snapshot) {

View File

@ -7,6 +7,7 @@ import 'package:image_picker/image_picker.dart';
import 'EditEvent.dart'; import 'EditEvent.dart';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../classes/auth_service.dart';
Future<void> main() async { Future<void> main() async {
// Ensure that plugin services are initialized so that `availableCameras()` // Ensure that plugin services are initialized so that `availableCameras()`
@ -45,10 +46,13 @@ class CameraEdit extends StatefulWidget {
class CameraEditState extends State<CameraEdit> { class CameraEditState extends State<CameraEdit> {
late CameraController _controller; late CameraController _controller;
late Future<void> _initializeControllerFuture; late Future<void> _initializeControllerFuture;
final AuthService _authService = AuthService();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_authService.checkTokenStatus(context);
// To display the current output from the Camera, // To display the current output from the Camera,
// create a CameraController. // create a CameraController.
@ -95,7 +99,6 @@ class CameraEditState extends State<CameraEdit> {
// camera preview. Use a FutureBuilder to display a loading spinner until the // camera preview. Use a FutureBuilder to display a loading spinner until the
// controller has finished initializing. // controller has finished initializing.
drawer: MyDrawer(), drawer: MyDrawer(),
body: FutureBuilder<void>( body: FutureBuilder<void>(
future: _initializeControllerFuture, future: _initializeControllerFuture,
builder: (context, snapshot) { builder: (context, snapshot) {

View File

@ -14,7 +14,13 @@ import 'dart:convert';
import '../variable/globals.dart' as globals; import '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart'; import '../classes/MyDrawer.dart';
void main() { import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
runApp(MyApp()); runApp(MyApp());
} }
@ -56,9 +62,21 @@ class DisplayPictureScreen extends StatefulWidget {
// A widget that displays the picture taken by the user. // A widget that displays the picture taken by the user.
class DisplayPictureScreenState extends State<DisplayPictureScreen> class DisplayPictureScreenState extends State<DisplayPictureScreen>
with ShowDescImageAdd, ShowAlertDialog, TickerProviderStateMixin { with ShowDescImageAdd, ShowAlertDialog, TickerProviderStateMixin {
BannerAd? _bannerAd;
final AuthService _authService = AuthService();
late AnimationController controller; late AnimationController controller;
@override @override
void initState() { void initState() {
super.initState();
_authService.checkTokenStatus(context);
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
setState(() {
_bannerAd = ad;
});
});
controller = AnimationController( controller = AnimationController(
/// [AnimationController]s can be created with `vsync: this` because of /// [AnimationController]s can be created with `vsync: this` because of
/// [TickerProviderStateMixin]. /// [TickerProviderStateMixin].
@ -68,7 +86,6 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
setState(() {}); setState(() {});
}); });
controller.repeat(reverse: false); controller.repeat(reverse: false);
super.initState();
_getEventInfosFromImage(); _getEventInfosFromImage();
} }
@ -85,6 +102,41 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
"L'IA de Google n'a pas su analyser l'image. Recommecer avec une autre"); "L'IA de Google n'a pas su analyser l'image. Recommecer avec une autre");
} }
void _showErrorDialog(BuildContext context, String title, String message) {
showAlertDialog(context, title, message);
}
Future<Map<String, dynamic>?> _fetchGeolocation(String place) async {
final apiKey = dotenv.env['PLACE_API_KEY'] ?? '';
final response = await http.get(Uri.parse(
'https://maps.googleapis.com/maps/api/place/textsearch/json?query=${place}}&key=$apiKey'));
if (response.statusCode == 200) {
final data = json.decode(response.body);
if (data['results'].isNotEmpty) {
return data['results'][0]['geometry']['location'];
}
}
return null;
}
Future<bool> _isDuplicateEvent(String accessToken,
Map<String, dynamic> jsonData, Map<String, dynamic> location) async {
final url = Uri.parse(
"${globals.api}/events/search?item=${jsonData["name"]}&date_event=${jsonData["start_date"]}"
"&min_lat=${location['lat']}&max_lat=${location['lat']}"
"&min_lon=${location['lng']}&max_lon=${location['lng']}");
final response = await http.get(url,
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'});
if (response.statusCode == 200) {
final events = jsonDecode(utf8.decode(response.bodyBytes));
return events.isNotEmpty;
}
return false;
}
Future<void> searchEvents(String json, String imagePath) async { Future<void> searchEvents(String json, String imagePath) async {
print(json.replaceAll("'''json", '').replaceAll("'''", "")); print(json.replaceAll("'''json", '').replaceAll("'''", ""));
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
@ -99,13 +151,22 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
var accessToken = prefs.getString("access_token") ?? ""; var accessToken = prefs.getString("access_token") ?? "";
if (accessToken.isNotEmpty) { if (accessToken.isNotEmpty) {
var urlGet = Uri.parse( final location = await _fetchGeolocation(place);
"${globals.api}/events/search?item=${name}&date_event=${date}"); if (location == null) {
_showErrorDialog(
context, "Erreur serveur", "Aucune donnée geographique");
return;
}
var responseGet = await http.get(urlGet, final url = Uri.parse(
headers: {HttpHeaders.cookieHeader: 'access_token=${accessToken}'}); "${globals.api}/events/search?item=${name}&date_event=${date}"
if (responseGet.statusCode == 200) { "&min_lat=${location['lat']}&max_lat=${location['lat']}"
var events = jsonDecode(utf8.decode(responseGet.bodyBytes)); "&min_lon=${location['lng']}&max_lon=${location['lng']}");
final response = await http.get(url,
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'});
if (response.statusCode == 200) {
var events = jsonDecode(utf8.decode(response.bodyBytes));
print("reponse http : ${events.length}"); print("reponse http : ${events.length}");
if (events.length == 0) { if (events.length == 0) {
Navigator.push( Navigator.push(
@ -121,7 +182,7 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
} }
} else { } else {
showAlertDialog(context, 'Erreur de reponse', showAlertDialog(context, 'Erreur de reponse',
"response status code update : ${responseGet.statusCode}"); "response status code update : ${response.statusCode}");
} }
} else { } else {
showAlertDialog(context, "Erreur de reponse", "Erreur de token"); showAlertDialog(context, "Erreur de reponse", "Erreur de token");
@ -145,7 +206,7 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
gemini gemini
.textAndImage( .textAndImage(
text: text:
"Peux-tu donner le nom, la date avec l'année actuelle ou d'une année future proche et le lieu de l'évènement sous format JSON (sans le caratère json au début de la chaine de caractère) avec les valeurs suivantes : name, place, description, tags (tableau sans espace), organizers (tableau), start_date et end_date (si le end_date est vide, alors donnez une valeur de six de plus par rapport à start_date) sous le format en YYYY-MM-DD HH:mm:ssZ", "Peux-tu donner le nom, la date (si l'année n'est pas précisé, mettez l'année actuelle ou future) et le lieu de l'évènement sous format JSON (sans le caratère json au début de la chaine de caractère) avec les valeurs suivantes : name, place, description, tags (tableau sans espace), organizers (tableau), start_date et end_date (si le end_date est vide, alors donnez une valeur de six de plus par rapport à start_date) sous le format en YYYY-MM-DD HH:mm:ssZ",
images: [file.readAsBytesSync()], images: [file.readAsBytesSync()],
modelName: "models/gemini-1.5-pro-latest") modelName: "models/gemini-1.5-pro-latest")
.then((value) => searchEvents( .then((value) => searchEvents(
@ -160,12 +221,17 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
// The image is stored as a file on the device. Use the `Image.file` // The image is stored as a file on the device. Use the `Image.file`
// constructor with the given path to display the image. // constructor with the given path to display the image.
drawer: MyDrawer(), drawer: MyDrawer(),
body: Padding( body: Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[ children: <Widget>[
_bannerAd == null
? SizedBox.shrink()
: SizedBox(
height: _bannerAd!.size.height.toDouble(),
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!)),
Text( Text(
'Analyse de l\'image en cours', 'Analyse de l\'image en cours',
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge,

View File

@ -9,12 +9,9 @@ import 'package:textfield_tags/textfield_tags.dart';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
import '../classes/events.dart'; import '../classes/events.dart';
import '../classes/MyDrawer.dart'; import '../classes/MyDrawer.dart';
import 'ItemMenu.dart';
import 'CameraEdit.dart';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import '../classes/alert.dart'; import '../classes/alert.dart';
@ -22,7 +19,13 @@ import '../classes/eventAdded.dart';
import '../variable/globals.dart' as globals; import '../variable/globals.dart' as globals;
void main() { import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
runApp(MyApp()); runApp(MyApp());
} }
@ -54,6 +57,9 @@ class EditEvent extends StatefulWidget {
class _EditEventState extends State<EditEvent> class _EditEventState extends State<EditEvent>
with ShowAlertDialog, ShowEventDialog { with ShowAlertDialog, ShowEventDialog {
BannerAd? _bannerAd;
final AuthService _authService = AuthService();
TextEditingController inputName = TextEditingController(); TextEditingController inputName = TextEditingController();
TextEditingController inputDate = TextEditingController(); TextEditingController inputDate = TextEditingController();
@ -159,177 +165,163 @@ class _EditEventState extends State<EditEvent>
} }
Future<void> _updateEvent(BuildContext context) async { Future<void> _updateEvent(BuildContext context) async {
var name = inputName.text; if (!_isEventInFuture()) {
var place = inputGeo.text; _showErrorDialog(context, "Erreur evenement", "Evenement non futur");
var description = inputDesc.text; return;
List<String> tags = List<String>.from(_stringTagController.getTags as List);
List<String> organizers =
List<String>.from(_stringOrgaController.getTags as List);
var startDateFormat = formatDate(startDatepicker.text);
//DateTime startDateCompare = DateTime.parse(startDateFormat);
DateTime dateNow = DateTime.now();
var endDateFormat = formatDate(endDatepicker.text);
var startDate =
"${startDateFormat}T${startTimepicker.text.replaceAll('-', ':')}";
var endDate = "${endDateFormat}T${endTimepicker.text.replaceAll('-', ':')}";
DateTime startDateCompare = DateTime.parse(startDate);
if (startDateCompare.isAfter(dateNow)) {
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
if (accessToken.isNotEmpty) {
try {
await dotenv.load();
final ApiTokenGoogle = dotenv.env['PLACE_API_KEY'] ?? '';
// Searchbox API for geocoding the place (No session token)
final searchboxUrl = Uri.parse(
'https://maps.googleapis.com/maps/api/place/textsearch/json?query=${place}&key=${ApiTokenGoogle}');
// Perform the request
final searchboxResponse = await http.get(searchboxUrl);
if (searchboxResponse.statusCode == 200) {
final data = json.decode(searchboxResponse.body);
print("data : ${data}");
if (data['results'].isNotEmpty) {
place = data['results'][0]['formatted_address'];
final coordinates = data['results'][0]['geometry']['location'];
final longitude = coordinates["lng"]; // Longitude
final latitude = coordinates["lat"]; // Latitude
var urlGet = Uri.parse(
"${globals.api}/events/search?item=${name}&date_event=${startDate}&min_lat=$latitude&max_lat=$latitude"
"&min_lon=$longitude&max_lon=$longitude");
var responseGet = await http.get(urlGet, headers: {
HttpHeaders.cookieHeader: 'access_token=${accessToken}'
});
if (responseGet.statusCode == 200) {
var events = jsonDecode(utf8.decode(responseGet.bodyBytes));
if (events.length > 0) {
if (events[0]["id"] != widget.events!.id) {
showAlertDialog(
context, "Info evenement", "Evenement deja existant");
}
return;
}
}
if (widget.imgPath.isNotEmpty) {
final params = {
'expiration': '15552000',
'key': dotenv.env["IMGBB_API_KEY"],
};
print("Post Img");
final urlPost = Uri.parse('https://api.imgbb.com/1/upload')
.replace(queryParameters: params);
File image = File(widget.imgPath);
Uint8List _bytes = await image.readAsBytes();
String _base64String = base64.encode(_bytes);
final req = http.MultipartRequest('POST', urlPost)
..fields['image'] = _base64String;
final stream = await req.send();
final res = await http.Response.fromStream(stream);
final status = res.statusCode;
print("code status imgbb ${status}");
if (status != 200) {
showAlertDialog(context, "Erreur image", "Image non posté");
return;
}
var body = json.decode(utf8.decode(res.bodyBytes));
imgUrl = body["data"]["url"];
}
var urlPut =
Uri.parse("${globals.api}/events/${widget.events!.id}");
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': name,
'place': place,
'start_date': startDate,
'end_date': endDate,
'organizers': organizers,
'latitude': latitude,
'longitude': longitude,
'description': description,
"imgUrl": imgUrl,
"tags": tags
}));
print(responsePut.statusCode);
if ((responsePut.statusCode == 200) ||
(responsePut.statusCode == 201)) {
showEventDialog(context, "Evenement ${name} modifie");
} else {
var text = "";
switch (responsePut.statusCode) {
case 400:
{
text = "Requête mal construite";
}
break;
case 406:
{
text = "Mot de passe incorrect";
}
break;
case 404:
{
text = "Utilisateur inconnu";
}
break;
case 403:
{
text = "Utilisateur desactive";
}
break;
case 410:
{
text = "Token invalide";
}
break;
case 500:
{
text = "Probleme interne du serveur";
}
break;
default:
{
text = "Probleme d'authentification inconnu";
}
break;
}
showAlertDialog(context, "Erreur serveur", text);
}
} else {
showAlertDialog(
context, "Erreur serveur", "Aucune donnée geographique");
}
} else {
showAlertDialog(context, "Erreur serveur", "Mapbox non accessible");
}
} catch (e) {
showAlertDialog(context, "Erreur serveur", "${e}");
}
} else {
showAlertDialog(context, "Erreur utilisateur", "Champ vide");
}
} else {
showAlertDialog(context, "Erreur evenement", "Evenement non futur");
} }
final accessToken = await _getAccessToken();
if (accessToken.isEmpty) {
_showErrorDialog(context, "Erreur utilisateur", "Champ vide");
return;
}
try {
await dotenv.load();
final geolocation = await _fetchGeolocation();
if (geolocation == null) {
_showErrorDialog(
context, "Erreur serveur", "Aucune donnée geographique");
return;
}
if (await _isDuplicateEvent(accessToken, geolocation)) {
_showErrorDialog(context, "Info evenement", "Evenement deja existant");
return;
}
if (widget.imgPath.isNotEmpty) {
imgUrl = await _uploadImage(widget.imgPath);
if (imgUrl.isEmpty) {
_showErrorDialog(context, "Erreur image", "Image non postée");
return;
}
}
await _updateEventData(accessToken, geolocation);
showEventDialog(context, "Evenement ${inputName.text} modifie");
} catch (e) {
_showErrorDialog(context, "Erreur serveur", "$e");
}
}
bool _isEventInFuture() {
DateTime startDateCompare = DateTime.parse(
"${formatDate(startDatepicker.text)}T${startTimepicker.text.replaceAll('-', ':')}");
return startDateCompare.isAfter(DateTime.now());
}
Future<String> _getAccessToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString("access_token") ?? "";
}
Future<Map<String, dynamic>?> _fetchGeolocation() async {
final apiKey = dotenv.env['PLACE_API_KEY'] ?? '';
final response = await http.get(Uri.parse(
'https://maps.googleapis.com/maps/api/place/textsearch/json?query=${inputGeo.text}&key=$apiKey'));
if (response.statusCode == 200) {
final data = json.decode(response.body);
if (data['results'].isNotEmpty) {
return data['results'][0]['geometry']['location'];
}
}
return null;
}
Future<bool> _isDuplicateEvent(
String accessToken, Map<String, dynamic> location) async {
final url = Uri.parse(
"${globals.api}/events/search?item=${inputName.text}&date_event=${formatDate(startDatepicker.text)}"
"&min_lat=${location['lat']}&max_lat=${location['lat']}"
"&min_lon=${location['lng']}&max_lon=${location['lng']}");
final response = await http.get(url,
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'});
if (response.statusCode == 200) {
final events = jsonDecode(utf8.decode(response.bodyBytes));
return events.isNotEmpty && events[0]["id"] != widget.events!.id;
}
return false;
}
Future<String> _uploadImage(String imagePath) async {
final params = {
'expiration': '15552000',
'key': dotenv.env["IMGBB_API_KEY"]
};
final url = Uri.parse('https://api.imgbb.com/1/upload')
.replace(queryParameters: params);
final image = File(imagePath);
final req = http.MultipartRequest('POST', url)
..fields['image'] = base64.encode(await image.readAsBytes());
final response = await http.Response.fromStream(await req.send());
return response.statusCode == 200
? json.decode(response.body)["data"]["url"]
: "";
}
Future<void> _updateEventData(
String accessToken, Map<String, dynamic> location) async {
final url = Uri.parse("${globals.api}/events/${widget.events!.id}");
final response = await http.put(url,
headers: {
HttpHeaders.cookieHeader: 'access_token=$accessToken',
HttpHeaders.acceptHeader: 'application/json, text/plain, */*',
HttpHeaders.contentTypeHeader: 'application/json'
},
body: jsonEncode({
'name': inputName.text,
'place': inputGeo.text,
'start_date':
"${formatDate(startDatepicker.text)}T${startTimepicker.text.replaceAll('-', ':')}",
'end_date':
"${formatDate(endDatepicker.text)}T${endTimepicker.text.replaceAll('-', ':')}",
'organizers':
List<String>.from(_stringOrgaController.getTags as List),
'latitude': location['lat'],
'longitude': location['lng'],
'description': inputDesc.text,
"imgUrl": imgUrl,
"tags": List<String>.from(_stringTagController.getTags as List)
}));
if (response.statusCode != 200 && response.statusCode != 201) {
_handleErrorResponse(context, response.statusCode);
}
}
void _handleErrorResponse(BuildContext context, int statusCode) {
final messages = {
400: "Requête mal construite",
406: "Mot de passe incorrect",
404: "Utilisateur inconnu",
403: "Utilisateur désactivé",
410: "Token invalide",
500: "Problème interne du serveur"
};
_showErrorDialog(context, "Erreur serveur",
messages[statusCode] ?? "Problème d'authentification inconnu");
}
void _showErrorDialog(BuildContext context, String title, String message) {
showAlertDialog(context, title, message);
} }
@override @override
void initState() { void initState() {
super.initState();
_authService.checkTokenStatus(context);
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
setState(() {
_bannerAd = ad;
});
});
inputName.text = widget.events!.name ?? ""; inputName.text = widget.events!.name ?? "";
startDatepicker.text = DateFormat("dd/MM/yyyy") startDatepicker.text = DateFormat("dd/MM/yyyy")
.format(DateTime.parse( .format(DateTime.parse(
@ -353,7 +345,6 @@ class _EditEventState extends State<EditEvent>
inputDesc.text = widget.events!.description ?? ""; inputDesc.text = widget.events!.description ?? "";
initialTags = List<String>.from(widget.events!.tags as List); initialTags = List<String>.from(widget.events!.tags as List);
initialOrga = List<String>.from(widget.events!.organizers as List); initialOrga = List<String>.from(widget.events!.organizers as List);
super.initState();
} }
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
@ -467,7 +458,6 @@ class _EditEventState extends State<EditEvent>
return Scaffold( return Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white,
drawer: MyDrawer(), drawer: MyDrawer(),
appBar: AppBar( appBar: AppBar(
title: Text("Add or Update a event"), title: Text("Add or Update a event"),
backgroundColor: Colors.blue, backgroundColor: Colors.blue,
@ -478,6 +468,12 @@ class _EditEventState extends State<EditEvent>
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
_bannerAd == null
? SizedBox.shrink()
: SizedBox(
height: _bannerAd!.size.height.toDouble(),
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!)),
if (widget.imgPath.isNotEmpty) if (widget.imgPath.isNotEmpty)
Padding( Padding(
padding: const EdgeInsets.only(top: 60.0), padding: const EdgeInsets.only(top: 60.0),

View File

@ -8,14 +8,20 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import '../classes/MyDrawer.dart'; import '../classes/MyDrawer.dart';
import '../main.dart'; import '../pages/LoginDemo.dart';
import '../classes/alert.dart'; import '../classes/alert.dart';
import '../classes/eventAdded.dart'; import '../classes/eventAdded.dart';
import '../variable/globals.dart' as globals; import '../variable/globals.dart' as globals;
void main() { import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
runApp(MyApp()); runApp(MyApp());
} }
@ -38,6 +44,9 @@ class EditProfile extends StatefulWidget {
class _EditProfileState extends State<EditProfile> class _EditProfileState extends State<EditProfile>
with ShowAlertDialog, ShowEventDialog { with ShowAlertDialog, ShowEventDialog {
BannerAd? _bannerAd;
final AuthService _authService = AuthService();
TextEditingController inputUserName = TextEditingController(); TextEditingController inputUserName = TextEditingController();
TextEditingController inputName = TextEditingController(); TextEditingController inputName = TextEditingController();
@ -192,6 +201,13 @@ class _EditProfileState extends State<EditProfile>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_authService.checkTokenStatus(context);
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
setState(() {
_bannerAd = ad;
});
});
_getInfoProfile(); _getInfoProfile();
} }
@ -215,6 +231,12 @@ class _EditProfileState extends State<EditProfile>
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
_bannerAd == null
? SizedBox.shrink()
: SizedBox(
height: _bannerAd!.size.height.toDouble(),
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!)),
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 15.0, right: 15.0, top: 15, bottom: 0), left: 15.0, right: 15.0, top: 15, bottom: 0),

View File

@ -10,7 +10,13 @@ import '../classes/MyDrawer.dart';
import '../classes/alert.dart'; import '../classes/alert.dart';
import '../classes/eventAdded.dart'; import '../classes/eventAdded.dart';
void main() { import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
runApp(MyApp()); runApp(MyApp());
} }
@ -33,6 +39,9 @@ class EditSettings extends StatefulWidget {
class _EditProfileState extends State<EditSettings> class _EditProfileState extends State<EditSettings>
with ShowAlertDialog, ShowEventDialog { with ShowAlertDialog, ShowEventDialog {
BannerAd? _bannerAd;
final AuthService _authService = AuthService();
TextEditingController inputUserName = TextEditingController(); TextEditingController inputUserName = TextEditingController();
int? kilometer; int? kilometer;
@ -54,6 +63,13 @@ class _EditProfileState extends State<EditSettings>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_authService.checkTokenStatus(context);
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
setState(() {
_bannerAd = ad;
});
});
getParameter(); getParameter();
} }
@ -71,6 +87,12 @@ class _EditProfileState extends State<EditSettings>
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
_bannerAd == null
? SizedBox.shrink()
: SizedBox(
height: _bannerAd!.size.height.toDouble(),
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!)),
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 15.0, left: 15.0,
@ -125,7 +147,7 @@ class _EditProfileState extends State<EditSettings>
child: TextButton( child: TextButton(
onPressed: () {}, onPressed: () {},
child: Text( child: Text(
'Mettre à jour les paramètres', 'Mettre à jour',
style: TextStyle(color: Colors.white, fontSize: 25), style: TextStyle(color: Colors.white, fontSize: 25),
), ),
), ),

View File

@ -0,0 +1,158 @@
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'dart:convert';
import 'dart:io';
import '../main.dart';
import '../classes/alert.dart';
import '../variable/globals.dart' as globals;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: PasswordForgot(),
);
}
}
class PasswordForgot extends StatefulWidget {
const PasswordForgot({super.key});
@override
_PasswordForgotState createState() => _PasswordForgotState();
}
class _PasswordForgotState extends State<PasswordForgot> with ShowAlertDialog {
TextEditingController inputEmail = TextEditingController();
convertNulltoEmptyString(var check) {
if (check == null) {
return "";
}
return check;
}
convertNulltoArray(List<String> check) {
if (check == null) {
return [];
}
return check;
}
String formatDate(String date) {
var splitedDate = date.split("/");
var day = splitedDate[0];
var month = splitedDate[1];
var year = splitedDate[2];
return "${year}-${month}-${day}";
}
Future<void> _forgotPassword(BuildContext context) async {
var email = inputEmail.text;
var urlPut = Uri.parse("${globals.api}/password/forgot");
var responsePost = await http.post(urlPut,
headers: {
HttpHeaders.acceptHeader: 'application/json, text/plain, */*',
HttpHeaders.contentTypeHeader: 'application/json'
},
body: jsonEncode({
'email': email,
}));
print(responsePost.statusCode);
if (responsePost.statusCode == 200) {
showAlertDialog(context, "Creation", "Un email a été envoyé à ${email}");
return;
}
final errorMessages = {
400: "Requête mal construite",
406: "Mot de passe incorrect",
404: "Utilisateur inconnu",
403: "Utilisateur désactivé",
410: "Token invalide",
500: "Problème interne du serveur",
};
final text = errorMessages[responsePost.statusCode] ??
"Problème d'authentification inconnu";
showAlertDialog(context, "Erreur serveur", text);
}
@override
void initState() {
super.initState();
}
final _formKey = GlobalKey<FormState>();
String? _validateField(String? value) {
return value!.isEmpty ? 'Champ requis' : null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Mot de passe oublie"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
body: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
left: 15.0, right: 15.0, top: 15, bottom: 0),
//padding: EdgeInsets.symmetric(horizontal: 15),
child: TextFormField(
controller: inputEmail,
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email',
hintText: 'Modifier l\'adresse mail'),
),
),
SizedBox(
height: 30,
),
Container(
height: 50,
width: 250,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(20)),
child: TextButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_forgotPassword(context);
}
},
child: Text(
'Envoyer le mail',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),
)
],
),
),
));
}
}

View File

@ -21,7 +21,13 @@ import 'MapboxPages.dart';
import 'ListItemByOrganizers.dart'; import 'ListItemByOrganizers.dart';
import 'EditEvent.dart'; import 'EditEvent.dart';
void main() { import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
initializeDateFormatting("fr_FR", null).then((_) => (const MyApp())); initializeDateFormatting("fr_FR", null).then((_) => (const MyApp()));
} }
@ -69,6 +75,9 @@ class ItemMenu extends StatefulWidget {
} }
class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog { class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
BannerAd? _bannerAd;
final AuthService _authService = AuthService();
String listUser = ""; String listUser = "";
String eventName = ""; String eventName = "";
String eventStartDate = ""; String eventStartDate = "";
@ -84,6 +93,13 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_authService.checkTokenStatus(context);
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
setState(() {
_bannerAd = ad;
});
});
_getEventInfos(); _getEventInfos();
} }
@ -174,6 +190,12 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
body: SingleChildScrollView( body: SingleChildScrollView(
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
_bannerAd == null
? SizedBox.shrink()
: SizedBox(
height: _bannerAd!.size.height.toDouble(),
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!)),
Padding( Padding(
padding: const EdgeInsets.only(top: 60.0), padding: const EdgeInsets.only(top: 60.0),
child: Image.network( child: Image.network(

View File

@ -11,10 +11,16 @@ import 'package:intl/date_symbol_data_local.dart';
import '../variable/globals.dart' as globals; import '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart'; import '../classes/MyDrawer.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
// app starting point // app starting point
void main() { void main() async {
initializeDateFormatting("fr_FR", null).then((_) => (const MyApp())); WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting("fr_FR", null);
runApp(const MyApp());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@ -23,6 +29,14 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('fr', 'FR'),
],
home: const ListItemOrganizers(organizer: "default"), home: const ListItemOrganizers(organizer: "default"),
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
); );
@ -41,15 +55,55 @@ class ListItemOrganizers extends StatefulWidget {
// homepage state // homepage state
class _MyHomePageState extends State<ListItemOrganizers> { class _MyHomePageState extends State<ListItemOrganizers> {
int _fetchCount = 0;
bool _isLoading = false;
late ScrollController _scrollController;
final AuthService _authService = AuthService();
void _incrementFetchCount() {
setState(() {
_fetchCount++;
});
}
void _scrollListener() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_incrementFetchCount();
_fetchData();
}
}
Future<void> _fetchData() async {
print("Counter : ${_fetchCount}");
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.delayed(Duration(seconds: 2));
getPosts(widget.organizer, count: _fetchCount);
setState(() {
_isLoading = false;
});
}
// variable to call and store future list of posts // variable to call and store future list of posts
// function to fetch data from api and return future list of posts // function to fetch data from api and return future list of posts
static Future<List<Events>> getPosts(organizer) async { static Future<List<Events>> getPosts(organizer, {count = 0}) async {
await initializeDateFormatting("fr_FR", null);
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? ""; var accessToken = prefs.getString("access_token") ?? "";
final List<Events> body = []; final List<Events> body = [];
if (accessToken.isNotEmpty) { if (accessToken.isNotEmpty) {
var url = Uri.parse("${globals.api}/events?organizers=${organizer}"); DateTime currentDatetime = DateTime.now();
num limit = 20 * (count + 1);
var url = Uri.parse(
"${globals.api}/events?organizers=${organizer}&limit=${limit}&current_datetime=${currentDatetime.toString()}");
final response = await http.get(url, headers: { final response = await http.get(url, headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=${accessToken}" HttpHeaders.cookieHeader: "access_token=${accessToken}"
@ -60,6 +114,20 @@ class _MyHomePageState extends State<ListItemOrganizers> {
return body; return body;
} }
@override
void initState() {
super.initState();
_authService.checkTokenStatus(context);
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// build function // build function
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -100,16 +168,20 @@ class _MyHomePageState extends State<ListItemOrganizers> {
foregroundColor: Colors.white, foregroundColor: Colors.white,
), ),
body: ListView.separated( body: ListView.separated(
itemCount: posts.length, controller: _scrollController,
itemCount: posts.isNotEmpty
? posts.length + (_isLoading ? 1 : 0) // Add 1 only if loading
: 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final post = posts[index]; final post = posts[index];
final startDate = DateTime.parse(post.startDate!); final startDate = DateTime.parse(post.startDate!);
final date = DateFormat.yMd().format(startDate);
final time = DateFormat.Hm().format(startDate); final dateLongue =
DateFormat('EEEE d MMMM y', 'fr_FR').format(startDate);
return ListTile( return ListTile(
title: Text('${post.name!}'), title: Text('${post.name!}'),
subtitle: Text('${post.place!}\n${date} ${time}'), subtitle: Text('${post.place!}\n${dateLongue}'),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,

View File

@ -12,10 +12,16 @@ import 'package:intl/date_symbol_data_local.dart';
import '../variable/globals.dart' as globals; import '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart'; import '../classes/MyDrawer.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
// app starting point // app starting point
void main() { void main() async {
initializeDateFormatting("fr_FR", null).then((_) => (const MyApp())); WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting("fr_FR", null);
runApp(const MyApp());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@ -24,6 +30,14 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('fr', 'FR'),
],
home: const ListItemTags(tags: "default"), home: const ListItemTags(tags: "default"),
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
); );
@ -43,13 +57,55 @@ class ListItemTags extends StatefulWidget {
class _MyHomePageState extends State<ListItemTags> { class _MyHomePageState extends State<ListItemTags> {
// variable to call and store future list of posts // variable to call and store future list of posts
int _fetchCount = 0;
bool _isLoading = false;
late ScrollController _scrollController;
final AuthService _authService = AuthService();
void _incrementFetchCount() {
setState(() {
_fetchCount++;
});
}
void _scrollListener() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_incrementFetchCount();
_fetchData();
}
}
Future<void> _fetchData() async {
print("Counter : ${_fetchCount}");
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.delayed(Duration(seconds: 2));
getPosts(widget.tags, count: _fetchCount);
setState(() {
_isLoading = false;
});
}
// function to fetch data from api and return future list of posts // function to fetch data from api and return future list of posts
static Future<List<Events>> getPosts(tags) async { static Future<List<Events>> getPosts(tags, {count = 0}) async {
await initializeDateFormatting("fr_FR", null);
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? ""; var accessToken = prefs.getString("access_token") ?? "";
final List<Events> body = []; final List<Events> body = [];
if (accessToken.isNotEmpty) { if (accessToken.isNotEmpty) {
var url = Uri.parse("${globals.api}/events?tags=${tags}"); DateTime currentDatetime = DateTime.now();
num limit = 20 * (count + 1);
var url = Uri.parse(
"${globals.api}/events?tags=${tags}&limit=${limit}&current_datetime=${currentDatetime.toString()}");
final response = await http.get(url, headers: { final response = await http.get(url, headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=${accessToken}" HttpHeaders.cookieHeader: "access_token=${accessToken}"
@ -60,6 +116,21 @@ class _MyHomePageState extends State<ListItemTags> {
return body; return body;
} }
@override
void initState() {
super.initState();
_authService.checkTokenStatus(context);
_authService.checkTokenStatus(context);
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// build function // build function
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -99,16 +170,19 @@ class _MyHomePageState extends State<ListItemTags> {
foregroundColor: Colors.white, foregroundColor: Colors.white,
), ),
body: ListView.separated( body: ListView.separated(
itemCount: posts.length, controller: _scrollController,
itemCount: posts.isNotEmpty
? posts.length + (_isLoading ? 1 : 0) // Add 1 only if loading
: 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final post = posts[index]; final post = posts[index];
final startDate = DateTime.parse(post.startDate!); final startDate = DateTime.parse(post.startDate!);
final date = DateFormat.yMd().format(startDate); final dateLongue =
final time = DateFormat.Hm().format(startDate); DateFormat('EEEE d MMMM y', 'fr_FR').format(startDate);
return ListTile( return ListTile(
title: Text('${post.name!}'), title: Text('${post.name!}'),
subtitle: Text('${post.place!}\n${date} ${time}'), subtitle: Text('${post.place!}\n${dateLongue}'),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,

View File

@ -16,8 +16,18 @@ import 'package:permission_handler/permission_handler.dart';
import "Camera.dart"; import "Camera.dart";
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
void main() { import '../classes/ad_helper.dart';
initializeDateFormatting("fr_FR", null).then((_) => runApp(const MyApp())); import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
await initializeDateFormatting("fr_FR", null);
runApp(const MyApp());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@ -26,6 +36,14 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('fr', 'FR'),
],
home: const ListItemMenu(), home: const ListItemMenu(),
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
); );
@ -40,6 +58,12 @@ class ListItemMenu extends StatefulWidget {
} }
class _MyHomePageState extends State<ListItemMenu> { class _MyHomePageState extends State<ListItemMenu> {
BannerAd? _bannerAd;
final AuthService _authService = AuthService();
late ScrollController _scrollController;
int _fetchCount = 0;
bool _isLoading = false;
Future<List<Events>> postsFuture = getPosts(); Future<List<Events>> postsFuture = getPosts();
List<Events> filteredPosts = []; List<Events> filteredPosts = [];
String geographicalZone = ''; String geographicalZone = '';
@ -62,6 +86,7 @@ class _MyHomePageState extends State<ListItemMenu> {
bool showInputTag = true; bool showInputTag = true;
// Fetching events from API // Fetching events from API
static Future<List<Events>> getPosts() async { static Future<List<Events>> getPosts() async {
await initializeDateFormatting("fr_FR");
PermissionStatus status = await Permission.location.status; PermissionStatus status = await Permission.location.status;
final List<Events> body = []; final List<Events> body = [];
var url = Uri.parse("${globals.api}/events"); var url = Uri.parse("${globals.api}/events");
@ -135,13 +160,59 @@ class _MyHomePageState extends State<ListItemMenu> {
return "${year}-${month}-${day}"; return "${year}-${month}-${day}";
} }
void _incrementFetchCount() {
setState(() {
_fetchCount++;
});
_fetchData();
}
void _scrollListener() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_incrementFetchCount();
}
// Scroll to top
}
Future<void> _fetchData() async {
print("Counter : ${_fetchCount}");
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.delayed(Duration(seconds: 2));
fetchPostsByLocation();
setState(() {
_isLoading = false;
});
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_authService.checkTokenStatus(context);
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
setState(() {
_bannerAd = ad;
});
});
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
// Initialize data fetch when the page loads // Initialize data fetch when the page loads
_getCurrentLocation(); _getCurrentLocation();
} }
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// Get the device's current location // Get the device's current location
Future<void> _getCurrentLocation() async { Future<void> _getCurrentLocation() async {
PermissionStatus status = await Permission.location.status; PermissionStatus status = await Permission.location.status;
@ -331,8 +402,10 @@ class _MyHomePageState extends State<ListItemMenu> {
} else { } else {
queryParameters = dateParameter; queryParameters = dateParameter;
} }
int limit = 20 * (_fetchCount + 1);
return Uri.parse("${globals.api}/$endpoint?$queryParameters"); return Uri.parse(
"${globals.api}/$endpoint?$queryParameters&limit=${limit}");
} }
Future<void> searchSuggestionsByItem(String input) async { Future<void> searchSuggestionsByItem(String input) async {
@ -343,7 +416,7 @@ class _MyHomePageState extends State<ListItemMenu> {
var url = await getUrlForEvents(); var url = await getUrlForEvents();
final response = await http.get(url, headers: { final response = await http.get(url, headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=$accessToken" HttpHeaders.cookieHeader: "acce0ss_token=$accessToken"
}); });
if (response.statusCode == 200) { if (response.statusCode == 200) {
@ -407,16 +480,17 @@ class _MyHomePageState extends State<ListItemMenu> {
if (response.statusCode == 200) { if (response.statusCode == 200) {
final List<dynamic> body = json.decode(utf8.decode(response.bodyBytes)); final List<dynamic> body = json.decode(utf8.decode(response.bodyBytes));
print("results fetch : ${body}"); print("results fetch : ${body}");
print("fetch count : ${_fetchCount}");
// Update state after getting the response // Update state after getting the response
setState(() { setState(() {
if (body.isNotEmpty) { int counter = filteredPosts.length;
// If we have results, map them to Events // If we have results, map them to Events
filteredPosts = body filteredPosts = body
.map((e) => Events.fromJson(e as Map<String, dynamic>)) .map((e) => Events.fromJson(e as Map<String, dynamic>))
.toList(); .toList();
} else { if (counter == filteredPosts.length) {
// If no results, clear filteredPosts _fetchCount--;
filteredPosts.clear();
} }
}); });
} else { } else {
@ -543,11 +617,19 @@ class _MyHomePageState extends State<ListItemMenu> {
drawer: MyDrawer(), drawer: MyDrawer(),
body: Column( body: Column(
children: [ children: [
_bannerAd == null
? SizedBox.shrink()
: SizedBox(
height: _bannerAd!.size.height.toDouble(),
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!),
),
if (showInputSearch) if (showInputSearch)
_buildSearchField( _buildSearchField(
controller: inputItem, controller: inputItem,
labelText: 'Search by item', labelText: 'Search by item',
onChanged: (value) { onChanged: (value) {
_fetchCount = 0;
if (value.isNotEmpty) { if (value.isNotEmpty) {
setState(() { setState(() {
itemName = value; itemName = value;
@ -565,6 +647,7 @@ class _MyHomePageState extends State<ListItemMenu> {
} }
}, },
onClear: () { onClear: () {
_fetchCount = 0;
setState(() { setState(() {
inputItem.clear(); inputItem.clear();
itemName = ''; itemName = '';
@ -576,6 +659,7 @@ class _MyHomePageState extends State<ListItemMenu> {
}, },
suggestions: suggestionsItem, suggestions: suggestionsItem,
onSuggestionTap: (suggestion) async { onSuggestionTap: (suggestion) async {
_fetchCount = 0;
setState(() { setState(() {
itemName = suggestion['name']; itemName = suggestion['name'];
inputItem.text = itemName; inputItem.text = itemName;
@ -591,6 +675,7 @@ class _MyHomePageState extends State<ListItemMenu> {
controller: inputTags, controller: inputTags,
labelText: 'Search by tags', labelText: 'Search by tags',
onChanged: (value) { onChanged: (value) {
_fetchCount = 0;
if (value.isNotEmpty) { if (value.isNotEmpty) {
setState(() { setState(() {
itemTags = value; itemTags = value;
@ -608,6 +693,7 @@ class _MyHomePageState extends State<ListItemMenu> {
} }
}, },
onClear: () { onClear: () {
_fetchCount = 0;
setState(() { setState(() {
inputTags.clear(); inputTags.clear();
}); });
@ -615,6 +701,8 @@ class _MyHomePageState extends State<ListItemMenu> {
}, },
suggestions: suggestionsTags, suggestions: suggestionsTags,
onSuggestionTap: (suggestion) async { onSuggestionTap: (suggestion) async {
_fetchCount = 0;
setState(() { setState(() {
itemTags = suggestion['name']; itemTags = suggestion['name'];
inputTags.text = itemTags; inputTags.text = itemTags;
@ -638,6 +726,8 @@ class _MyHomePageState extends State<ListItemMenu> {
controller: inputGeo, controller: inputGeo,
labelText: 'Search by geographical zone', labelText: 'Search by geographical zone',
onChanged: (value) async { onChanged: (value) async {
_fetchCount = 0;
if (value.isNotEmpty) { if (value.isNotEmpty) {
setState(() { setState(() {
geographicalZone = value; geographicalZone = value;
@ -659,6 +749,7 @@ class _MyHomePageState extends State<ListItemMenu> {
} }
}, },
onClear: () async { onClear: () async {
_fetchCount = 0;
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
prefs.remove("city_lat"); prefs.remove("city_lat");
prefs.remove("city_long"); prefs.remove("city_long");
@ -674,6 +765,7 @@ class _MyHomePageState extends State<ListItemMenu> {
}, },
suggestions: suggestionsGeo, suggestions: suggestionsGeo,
onSuggestionTap: (suggestion) async { onSuggestionTap: (suggestion) async {
_fetchCount = 0;
final latitude = suggestion['geometry']['coordinates'][1]; final latitude = suggestion['geometry']['coordinates'][1];
final longitude = suggestion['geometry']['coordinates'][0]; final longitude = suggestion['geometry']['coordinates'][0];
setState(() { setState(() {
@ -748,15 +840,26 @@ class _MyHomePageState extends State<ListItemMenu> {
} }
return ListView.separated( return ListView.separated(
itemCount: displayedPosts.length, controller: _scrollController,
itemCount: displayedPosts.isNotEmpty
? displayedPosts.length +
(_isLoading ? 1 : 0) // Add 1 only if loading
: 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index >= displayedPosts.length) {
return _isLoading
? Center(child: CircularProgressIndicator())
: SizedBox.shrink();
}
final post = displayedPosts[index]; final post = displayedPosts[index];
final startDate = DateTime.parse(post.startDate!); final startDate = DateTime.parse(post.startDate!);
final date = DateFormat.yMd().format(startDate); //final date = DateFormat.yMd().format(startDate);
final time = DateFormat.Hm().format(startDate); //final time = DateFormat.Hm().format(startDate);
final dateLongue =
DateFormat('EEEE d MMMM y', 'fr_FR').format(startDate);
return ListTile( return ListTile(
title: Text('${post.name!}'), title: Text('${post.name!}'),
subtitle: Text('${post.place!}\n${date} ${time}'), subtitle: Text('${post.place!}\n${dateLongue}'),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,

View File

@ -0,0 +1,154 @@
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:permission_handler/permission_handler.dart';
import '../classes/auth_service.dart';
import '../pages/ListItemMenu.dart';
import '../pages/AddProfile.dart';
import '../pages/ForgotPassword.dart';
import '../classes/alert.dart';
import '../classes/ad_helper.dart';
class LoginDemo extends StatefulWidget {
@override
_LoginDemoState createState() => _LoginDemoState();
}
class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
BannerAd? _bannerAd;
TextEditingController inputPseudo = TextEditingController();
TextEditingController inputPassword = TextEditingController();
final AuthService _authService = AuthService();
bool _rememberMe = false;
Future<void> _login(BuildContext context) async {
final pseudo = inputPseudo.text;
final password = inputPassword.text;
if (pseudo.isEmpty || password.isEmpty) {
showAlertDialog(context, "Erreur", "Champ vide");
return;
}
bool success =
await _authService.login(pseudo, password, rememberMe: _rememberMe);
if (success) {
Navigator.push(
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
} else {
showAlertDialog(context, "Erreur", "Échec de l'authentification");
}
}
@override
void initState() {
super.initState();
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
setState(() {
_bannerAd = ad;
});
});
_checkLocationPermission();
_checkLoginStatus();
}
Future<void> _checkLoginStatus() async {
bool loggedIn = await _authService.isLoggedIn();
if (loggedIn) {
Navigator.push(
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
}
}
Future<void> _checkLocationPermission() async {
PermissionStatus status = await Permission.location.status;
if (!status.isGranted) {
await Permission.location.request();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Login Page"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
_bannerAd == null
? SizedBox.shrink()
: SizedBox(
height: _bannerAd!.size.height.toDouble(),
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 15),
child: TextField(
controller: inputPseudo,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Pseudo',
hintText: 'Enter pseudo existant',
),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 15),
child: TextField(
controller: inputPassword,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter secure password',
),
),
),
CheckboxListTile(
title: Text("Se souvenir de moi"),
value: _rememberMe,
onChanged: (newValue) {
setState(() {
_rememberMe = newValue ?? false;
});
},
),
TextButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) => PasswordForgot()));
},
child: Text('Forgot Password',
style: TextStyle(color: Colors.blue, fontSize: 15)),
),
Container(
height: 50,
width: 250,
decoration: BoxDecoration(
color: Colors.blue, borderRadius: BorderRadius.circular(20)),
child: TextButton(
onPressed: () => _login(context),
child: Text('Login',
style: TextStyle(color: Colors.white, fontSize: 25)),
),
),
SizedBox(height: 130),
InkWell(
child: Text('New User? Create Account'),
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (_) => AddProfile()));
},
),
],
),
),
);
}
}

View File

@ -11,6 +11,7 @@ import 'package:geolocator/geolocator.dart'; // For getting the user's location
import '../classes/alert.dart'; // Assuming this contains your error dialog code. import '../classes/alert.dart'; // Assuming this contains your error dialog code.
import '../variable/globals.dart' as globals; import '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart'; import '../classes/MyDrawer.dart';
import '../classes/auth_service.dart';
void main() async { void main() async {
await dotenv.load(fileName: ".env"); // Load .env file await dotenv.load(fileName: ".env"); // Load .env file
@ -44,6 +45,8 @@ class MapboxPages extends StatefulWidget {
} }
class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog { class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
final AuthService _authService = AuthService();
late MapboxMapController mapController; late MapboxMapController mapController;
late String mapboxAccessToken; late String mapboxAccessToken;
List<LatLng> routeCoordinates = []; List<LatLng> routeCoordinates = [];
@ -58,6 +61,8 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_authService.checkTokenStatus(context);
_getUserLocation(); _getUserLocation();
} }

View File

@ -16,7 +16,13 @@ import '../classes/eventAdded.dart';
import '../variable/globals.dart' as globals; import '../variable/globals.dart' as globals;
void main() { import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
runApp(MyApp()); runApp(MyApp());
} }
@ -45,6 +51,9 @@ class UpdateeventImage extends StatefulWidget {
class _UpdateeventImageState extends State<UpdateeventImage> class _UpdateeventImageState extends State<UpdateeventImage>
with ShowAlertDialog, ShowEventDialog { with ShowAlertDialog, ShowEventDialog {
BannerAd? _bannerAd;
final AuthService _authService = AuthService();
TextEditingController inputName = TextEditingController(); TextEditingController inputName = TextEditingController();
TextEditingController inputDate = TextEditingController(); TextEditingController inputDate = TextEditingController();
@ -308,8 +317,15 @@ class _UpdateeventImageState extends State<UpdateeventImage>
@override @override
void initState() { void initState() {
start();
super.initState(); super.initState();
_authService.checkTokenStatus(context);
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
setState(() {
_bannerAd = ad;
});
});
start();
} }
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
@ -426,6 +442,12 @@ class _UpdateeventImageState extends State<UpdateeventImage>
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
_bannerAd == null
? SizedBox.shrink()
: SizedBox(
height: _bannerAd!.size.height.toDouble(),
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!)),
Padding( Padding(
padding: const EdgeInsets.only(top: 60.0), padding: const EdgeInsets.only(top: 60.0),
child: Center( child: Center(

View File

@ -10,6 +10,7 @@ import geolocator_apple
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation import shared_preferences_foundation
import url_launcher_macos import url_launcher_macos
import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
@ -17,4 +18,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
} }

View File

@ -9,6 +9,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.6.1" version: "3.6.1"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
url: "https://pub.dev"
source: hosted
version: "1.5.8"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -89,6 +105,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.18.0" version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
@ -137,6 +161,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
encrypt:
dependency: transitive
description:
name: encrypt
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
url: "https://pub.dev"
source: hosted
version: "5.0.3"
encrypt_shared_preferences:
dependency: "direct main"
description:
name: encrypt_shared_preferences
sha256: "35cd218e5e9d12fe4a63a545f46f2144d861909e4c4f2c4606fc75ffb53d9a46"
url: "https://pub.dev"
source: hosted
version: "0.8.8"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -230,6 +270,11 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "4.0.0"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@ -304,6 +349,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.3" version: "0.2.3"
google_mobile_ads:
dependency: "direct main"
description:
name: google_mobile_ads
sha256: "0d4a3744b5e8ed1b8be6a1b452d309f811688855a497c6113fc4400f922db603"
url: "https://pub.dev"
source: hosted
version: "5.3.1"
http: http:
dependency: "direct main" dependency: "direct main"
description: description:
@ -640,6 +693,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.dev"
source: hosted
version: "3.9.1"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
@ -877,6 +938,38 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
webview_flutter:
dependency: transitive
description:
name: webview_flutter
sha256: "889a0a678e7c793c308c68739996227c9661590605e70b1f6cf6b9a6634f7aec"
url: "https://pub.dev"
source: hosted
version: "4.10.0"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
sha256: "512c26ccc5b8a571fd5d13ec994b7509f142ff6faf85835e243dde3538fdc713"
url: "https://pub.dev"
source: hosted
version: "4.3.2"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d
url: "https://pub.dev"
source: hosted
version: "2.10.0"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: d183aa3d0fbc1f4d0715ce06c5a44cad636598c3340ae8d359fbc61b4016fb60
url: "https://pub.dev"
source: hosted
version: "3.18.3"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View File

@ -29,6 +29,9 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_localizations:
sdk: flutter
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
@ -50,6 +53,8 @@ dependencies:
permission_handler: ^11.3.1 permission_handler: ^11.3.1
url_launcher: ^6.3.1 url_launcher: ^6.3.1
mapbox_gl: ^0.16.0 mapbox_gl: ^0.16.0
google_mobile_ads: ^5.3.1
encrypt_shared_preferences: ^0.8.8
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -67,6 +72,7 @@ dev_dependencies:
# The following section is specific to Flutter packages. # The following section is specific to Flutter packages.
flutter: flutter:
generate: true
# The following line ensures that the Material Icons font is # The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in # included with your application, so that you can use the icons in