diff --git a/covas_mobile/lib/pages/ListItemMenu.dart b/covas_mobile/lib/pages/ListItemMenu.dart index bb5d87a..e9fee28 100644 --- a/covas_mobile/lib/pages/ListItemMenu.dart +++ b/covas_mobile/lib/pages/ListItemMenu.dart @@ -1,4 +1,3 @@ -import 'package:covas_mobile/classes/alert.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv @@ -47,6 +46,8 @@ class _MyHomePageState extends State { String query = ''; List> suggestions = []; TextEditingController inputGeo = TextEditingController(); + TextEditingController startDatepicker = TextEditingController(); + TextEditingController endDatepicker = TextEditingController(); // Fetching events from API static Future> getPosts() async { @@ -112,6 +113,15 @@ class _MyHomePageState extends State { return body; } + String formatDate(String date) { + var splitedDate = date.split("-"); + var day = splitedDate[0]; + var month = splitedDate[1]; + var year = splitedDate[2]; + + return "${year}-${month}-${day}"; + } + // Fetching events from API Future> getAllPosts() async { SharedPreferences prefs = await SharedPreferences.getInstance(); @@ -121,6 +131,21 @@ class _MyHomePageState extends State { DateTime currentDateTime = DateTime.now(); var url = Uri.parse( "${globals.api}/events?current_datetime=${currentDateTime.toString()}"); + if (startDatepicker.text.isNotEmpty) { + var date = DateTime.parse(formatDate(startDatepicker.text)); + url = Uri.parse("${globals.api}/events?date_event=${date.toString()}"); + } + if (endDatepicker.text.isNotEmpty) { + var date = DateTime.parse(formatDate(endDatepicker.text)); + url = Uri.parse("${globals.api}/events?date_event=${date.toString()}"); + } + if ((startDatepicker.text.isNotEmpty) && + (endDatepicker.text.isNotEmpty)) { + var start_date = DateTime.parse(formatDate(startDatepicker.text)); + var end_date = DateTime.parse(formatDate(endDatepicker.text)); + url = Uri.parse( + "${globals.api}/events?start_date=${start_date.toString()}&end_date=${end_date.toString()}"); + } final response = await http.get(url, headers: { "Content-Type": "application/json", HttpHeaders.cookieHeader: "access_token=${accessToken}" @@ -146,19 +171,32 @@ class _MyHomePageState extends State { print("Location permission granted"); // Get the current position with high accuracy - LocationSettings locationSettings = LocationSettings( - accuracy: LocationAccuracy.high, - distanceFilter: - 10, // Optional: Minimum distance (in meters) to trigger location update - ); - - Position position = await Geolocator.getCurrentPosition( - locationSettings: locationSettings, - ); + const LocationSettings locationSettings = LocationSettings( + accuracy: LocationAccuracy.medium, timeLimit: Duration(seconds: 5)); + Position? position; + try { + position = await Geolocator.getCurrentPosition( + locationSettings: locationSettings); + } on LocationServiceDisabledException { + // Handle location services disabled + print('Location services are disabled.'); + position = await Geolocator.getLastKnownPosition(); + if (position == null) { + print('No last known position available.'); + } + } catch (e) { + // Handle other errors + print('Failed to get location: $e'); + position = await Geolocator.getLastKnownPosition(); + if (position == null) { + print('No last known position available.'); + } + } // Reverse geocode: Get city and country from latitude and longitude using Mapbox Search API - final place = - await _getCityAndCountry(position.latitude, position.longitude); + if (position != null) { + _getCityAndCountry(position!.latitude, position!.longitude); + } } } @@ -189,7 +227,10 @@ class _MyHomePageState extends State { String country = _getCountryFromFeatures(features); print("city : ${city} ${country}"); if (city.isNotEmpty && country.isNotEmpty) { - fetchPostsByLocation(latitude, longitude); + SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setDouble("city_lat", latitude); + prefs.setDouble("city_long", longitude); + fetchPostsByLocation(); setState(() { inputGeo.text = "${city}, ${country}"; }); @@ -269,11 +310,14 @@ class _MyHomePageState extends State { } } - Future fetchPostsByLocation(double latitude, double longitude) async { + Future fetchPostsByLocation() async { SharedPreferences prefs = await SharedPreferences.getInstance(); var accessToken = prefs.getString("access_token") ?? ""; if (accessToken.isNotEmpty) { + double latitude = prefs.getDouble("city_lat") ?? 0.0; + double longitude = prefs.getDouble("city_long") ?? 0.0; + // Calculate the boundaries double radiusInKm = 50; double latDistance = radiusInKm / 111.0; @@ -284,10 +328,35 @@ class _MyHomePageState extends State { double minLon = longitude - lonDistance; double maxLon = longitude + lonDistance; DateTime currentDate = DateTime.now(); + print("current_date : ${currentDate}"); var url = Uri.parse("${globals.api}/events/search" "?min_lat=$minLat&max_lat=$maxLat" "&min_lon=$minLon&max_lon=$maxLon¤t_datetime=${currentDate.toString()}"); - + if (startDatepicker.text.isNotEmpty) { + var date = DateTime.parse(formatDate(startDatepicker.text)); + url = Uri.parse("${globals.api}/events/search" + "?min_lat=$minLat&max_lat=$maxLat" + "&min_lon=$minLon&max_lon=$maxLon&date_event=" + + date.toString()); + } + if (endDatepicker.text.isNotEmpty) { + var date = DateTime.parse(formatDate(endDatepicker.text)); + url = Uri.parse("${globals.api}/events/search" + "?min_lat=$minLat&max_lat=$maxLat" + "&min_lon=$minLon&max_lon=$maxLon&date_event=" + + date.toString()); + } + if ((startDatepicker.text.isNotEmpty) && + (endDatepicker.text.isNotEmpty)) { + var start_date = DateTime.parse(formatDate(startDatepicker.text)); + var end_date = DateTime.parse(formatDate(endDatepicker.text)); + url = Uri.parse("${globals.api}/events/search" + "?min_lat=$minLat&max_lat=$maxLat" + "&min_lon=$minLon&max_lon=$maxLon&start_date=" + + start_date.toString() + + "&end_date=" + + end_date.toString()); + } final response = await http.get(url, headers: { "Content-Type": "application/json", HttpHeaders.cookieHeader: "access_token=$accessToken" @@ -314,6 +383,86 @@ class _MyHomePageState extends State { } } + onTapFunctionDatePicker( + {required BuildContext context, String position = ""}) async { + DateTime dateEvent = DateTime.now(); + if (startDatepicker.text.isNotEmpty) { + dateEvent = DateTime.parse(formatDate(startDatepicker.text)); + } + + DateTime? pickedDate = await showDatePicker( + context: context, firstDate: dateEvent, lastDate: DateTime(2104)); + if (pickedDate == null) return; + if (position == "start") { + startDatepicker.text = DateFormat("dd-MM-yyyy").format(pickedDate); + } else if (position == "end") { + endDatepicker.text = DateFormat("dd-MM-yyyy").format(pickedDate); + } + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (startDatepicker.text.isNotEmpty) { + prefs.setString("date_event", startDatepicker.text); + prefs.setString("start_date", startDatepicker.text); + } + if (endDatepicker.text.isNotEmpty) { + prefs.setString("date_event", endDatepicker.text); + prefs.setString("end_date", endDatepicker.text); + } + if ((startDatepicker.text.isNotEmpty) && (endDatepicker.text.isNotEmpty)) { + prefs.remove("date_event"); + } + if (inputGeo.text.isEmpty) { + _fetchInitialData(); + } else { + fetchPostsByLocation(); + } + } + + Padding _buildDateField(String position) { + TextEditingController datePicker = startDatepicker; + String hintText = "Date de début"; + if (position == "end") { + datePicker = endDatepicker; + hintText = "Date de fin"; + } + return Padding( + padding: const EdgeInsets.all(8.0), + //padding: EdgeInsets.symmetric(horizontal: 15), + child: TextFormField( + controller: datePicker, + readOnly: true, + decoration: InputDecoration( + border: OutlineInputBorder(), + suffixIcon: IconButton( + icon: const Icon(Icons.clear), + onPressed: () async { + setState(() { + datePicker.text = ''; + }); + SharedPreferences prefs = + await SharedPreferences.getInstance(); + if (position == "start") { + prefs.remove("start_date"); + } + if (position == "end") { + prefs.remove("end_date"); + } + if ((startDatepicker.text.isEmpty) && + (endDatepicker.text.isEmpty)) { + prefs.remove("date_event"); + } + if (inputGeo.text.isNotEmpty) { + fetchPostsByLocation(); + } else { + _fetchInitialData(); + } + }, + ), + hintText: hintText), + onTap: () => + onTapFunctionDatePicker(context: context, position: position)), + ); + } + Padding _buildGeographicalZoneSearchField() { return Padding( padding: const EdgeInsets.all(8.0), @@ -326,7 +475,11 @@ class _MyHomePageState extends State { border: OutlineInputBorder(), suffixIcon: IconButton( icon: const Icon(Icons.clear), - onPressed: () { + onPressed: () async { + SharedPreferences prefs = + await SharedPreferences.getInstance(); + prefs.remove("city_lat"); + prefs.remove("city_long"); setState(() { inputGeo.clear(); // Clear the text field geographicalZone = ''; // Reset the geographical zone state @@ -367,8 +520,11 @@ class _MyHomePageState extends State { inputGeo.text = geographicalZone; suggestions.clear(); }); - - await fetchPostsByLocation(latitude, longitude); + SharedPreferences prefs = + await SharedPreferences.getInstance(); + prefs.setDouble("city_lat", latitude); + prefs.setDouble("city_long", longitude); + await fetchPostsByLocation(); }, ); }, @@ -397,7 +553,7 @@ class _MyHomePageState extends State { onPressed: () { showSearch( context: context, - delegate: SearchDelegateExample(geoQuery: inputGeo.text), + delegate: SearchDelegateExample(), ); }, ), @@ -406,6 +562,8 @@ class _MyHomePageState extends State { body: Column( children: [ _buildGeographicalZoneSearchField(), + _buildDateField("start"), + _buildDateField("end"), Expanded( child: FutureBuilder>( future: postsFuture, @@ -455,7 +613,6 @@ class _MyHomePageState extends State { final startDate = DateTime.parse(post.startDate!); final date = DateFormat.yMd().format(startDate); final time = DateFormat.Hm().format(startDate); - return ListTile( title: Text('${post.name!}'), subtitle: Text('${post.place!}\n${date} ${time}'), diff --git a/covas_mobile/lib/pages/SearchDelegate.dart b/covas_mobile/lib/pages/SearchDelegate.dart index e49b17e..c7c2fcb 100644 --- a/covas_mobile/lib/pages/SearchDelegate.dart +++ b/covas_mobile/lib/pages/SearchDelegate.dart @@ -11,11 +11,7 @@ import 'dart:math'; import '../variable/globals.dart' as globals; class SearchDelegateExample extends SearchDelegate { - final String geoQuery; - - SearchDelegateExample({ - required this.geoQuery, - }); + SearchDelegateExample(); @override List buildActions(BuildContext context) { @@ -43,7 +39,7 @@ class SearchDelegateExample extends SearchDelegate { Widget buildResults(BuildContext context) { // Perform the search and return the results return FutureBuilder>( - future: searchPosts(query, geoQuery), + future: searchPosts(query), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); @@ -79,45 +75,51 @@ class SearchDelegateExample extends SearchDelegate { return Container(); // Implement suggestions if needed } - Future> searchPosts(String query, String geoQuery) async { + 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> searchPosts(String query) async { SharedPreferences prefs = await SharedPreferences.getInstance(); var accessToken = prefs.getString("access_token") ?? ""; + var latitude = prefs.getDouble("city_lat") ?? 0.0; + var longitude = prefs.getDouble("city_long") ?? 0.0; final List body = []; if (accessToken.isNotEmpty) { - DateTime currentDate = DateTime.now(); + String parameter = "current_datetime"; + String currentDate = DateTime.now().toString(); + String date = prefs.getString("date_event") ?? ""; + String startDate = prefs.getString("start_date") ?? ""; + String endDate = prefs.getString("end_date") ?? ""; + if (date.isNotEmpty) { + currentDate = DateTime.parse(formatDate(date)).toString(); + parameter = "date_event"; + } + String parameterString = "${parameter}=${currentDate}"; + if ((startDate.isNotEmpty) && (endDate.isNotEmpty)) { + startDate = DateTime.parse(formatDate(startDate)).toString(); + endDate = DateTime.parse(formatDate(endDate)).toString(); + parameterString = "start_date=${startDate}&end_date=${endDate}"; + } var url = Uri.parse( - "${globals.api}/events/search?item=${query}¤t_dateime=${currentDate.toString()}"); - if (geoQuery.isNotEmpty) { - await dotenv.load( - fileName: ".env"); // Load your .env for the Mapbox access token - final mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''; - final geocodeUrl = Uri.parse( - 'https://api.mapbox.com/geocoding/v5/mapbox.places/$geoQuery.json?access_token=$mapboxAccessToken'); - final geocodeResponse = await http.get(geocodeUrl); - if (geocodeResponse.statusCode == 200) { - final geocodeData = json.decode(geocodeResponse.body); - if (geocodeData['features'].isNotEmpty) { - final coordinates = - geocodeData['features'][0]['geometry']['coordinates']; - final longitude = coordinates[0]; // Longitude - final latitude = coordinates[1]; // Latitude - - // Now use the latitude and longitude to get events within a 50km radius - double radiusInKm = 50; - double latDistance = radiusInKm / 111.0; - double lonDistance = - radiusInKm / (111.0 * cos(latitude * pi / 180)); - - double minLat = latitude - latDistance; - double maxLat = latitude + latDistance; - double minLon = longitude - lonDistance; - double maxLon = longitude + lonDistance; - - // Construct the search URL with the item query and latitude/longitude bounds - url = Uri.parse( - "${globals.api}/events/search?item=${query}&min_lat=${minLat}&max_lat=${maxLat}&min_lon=${minLon}&max_lon=${maxLon}¤t_dateime=${currentDate.toString()}"); - } - } + "${globals.api}/events/search?item=${query}&${parameterString}"); + if ((latitude != 0.0) && (longitude != 0.0)) { + // Now use the latitude and longitude to get events within a 50km radius + double radiusInKm = 50; + double latDistance = radiusInKm / 111.0; + double lonDistance = radiusInKm / (111.0 * cos(latitude * pi / 180)); + double minLat = latitude - latDistance; + double maxLat = latitude + latDistance; + double minLon = longitude - lonDistance; + double maxLon = longitude + lonDistance; + // Construct the search URL with the item query and latitude/longitude bounds + url = Uri.parse( + "${globals.api}/events/search?item=${query}&min_lat=${minLat}&max_lat=${maxLat}&min_lon=${minLon}&max_lon=${maxLon}&${parameterString}"); } final response = await http.get(url, headers: { "Content-Type": "application/json",