Merge pull request 'feature/input-date' (#19) from feature/input-date into main

Reviewed-on: #19
This commit is contained in:
v4l3n71n 2024-11-23 21:54:00 +01:00
commit 6b3bf5004c
2 changed files with 219 additions and 60 deletions

View File

@ -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<ListItemMenu> {
String query = '';
List<Map<String, dynamic>> suggestions = [];
TextEditingController inputGeo = TextEditingController();
TextEditingController startDatepicker = TextEditingController();
TextEditingController endDatepicker = TextEditingController();
// Fetching events from API
static Future<List<Events>> getPosts() async {
@ -112,6 +113,15 @@ class _MyHomePageState extends State<ListItemMenu> {
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<List<Events>> getAllPosts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
@ -121,6 +131,21 @@ class _MyHomePageState extends State<ListItemMenu> {
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<ListItemMenu> {
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<ListItemMenu> {
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<ListItemMenu> {
}
}
Future<void> fetchPostsByLocation(double latitude, double longitude) async {
Future<void> 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<ListItemMenu> {
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&current_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<ListItemMenu> {
}
}
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<ListItemMenu> {
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<ListItemMenu> {
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<ListItemMenu> {
onPressed: () {
showSearch(
context: context,
delegate: SearchDelegateExample(geoQuery: inputGeo.text),
delegate: SearchDelegateExample(),
);
},
),
@ -406,6 +562,8 @@ class _MyHomePageState extends State<ListItemMenu> {
body: Column(
children: [
_buildGeographicalZoneSearchField(),
_buildDateField("start"),
_buildDateField("end"),
Expanded(
child: FutureBuilder<List<Events>>(
future: postsFuture,
@ -455,7 +613,6 @@ class _MyHomePageState extends State<ListItemMenu> {
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}'),

View File

@ -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<Widget> buildActions(BuildContext context) {
@ -43,7 +39,7 @@ class SearchDelegateExample extends SearchDelegate {
Widget buildResults(BuildContext context) {
// Perform the search and return the results
return FutureBuilder<List<Events>>(
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<List<Events>> 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<List<Events>> 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<Events> 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}&current_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}&current_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",