From 03f3e1c55b545d6b5766308f08f926cc82d12c14 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Sun, 22 Dec 2024 22:11:20 +0100 Subject: [PATCH] =?UTF-8?q?add=20suggestion=20item=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- covas_mobile/lib/pages/ListItemMenu.dart | 33 +- covas_mobile/lib/pages/UpdateEventImage.dart | 338 +++++++++---------- 2 files changed, 183 insertions(+), 188 deletions(-) diff --git a/covas_mobile/lib/pages/ListItemMenu.dart b/covas_mobile/lib/pages/ListItemMenu.dart index b4fcfd6..1bf80a2 100644 --- a/covas_mobile/lib/pages/ListItemMenu.dart +++ b/covas_mobile/lib/pages/ListItemMenu.dart @@ -44,6 +44,7 @@ class _MyHomePageState extends State { List filteredPosts = []; String geographicalZone = ''; String itemName = ''; + String itemTags = ''; String query = ''; List> suggestions = []; List> suggestionsItem = []; @@ -376,8 +377,9 @@ class _MyHomePageState extends State { suggestionsTags = (data as List) .map((feature) => {'name': feature['name']}) .toList(); - if (suggestionsItem.isNotEmpty) { - showDateFields = false; + if (suggestionsTags.isNotEmpty) { + showInputGeo = false; + showInputSearch = false; showArrow = false; } }); @@ -602,6 +604,33 @@ class _MyHomePageState extends State { fetchPostsByLocation(); } }), + if (suggestionsTags.isNotEmpty) + Container( + height: 200, + decoration: BoxDecoration( + border: Border.all(color: Colors.blue), + borderRadius: BorderRadius.circular(8), + ), + child: ListView.builder( + shrinkWrap: true, + itemCount: suggestions.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(suggestionsTags[index]['name']), + onTap: () async { + setState(() { + itemTags = suggestionsTags[index]['name']; + inputTags.text = itemTags; + suggestionsTags.clear(); + showArrow = true; + showInputSearch = true; + showInputGeo = true; + }); + }, + ); + }, + ), + ), ], ), ); diff --git a/covas_mobile/lib/pages/UpdateEventImage.dart b/covas_mobile/lib/pages/UpdateEventImage.dart index bfadf0d..1f544ca 100644 --- a/covas_mobile/lib/pages/UpdateEventImage.dart +++ b/covas_mobile/lib/pages/UpdateEventImage.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:http/http.dart' as http; +import 'package:uuid/uuid.dart'; import 'package:intl/intl.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:textfield_tags/textfield_tags.dart'; @@ -138,6 +139,7 @@ class _UpdateeventImageState extends State } Future _updateEvent(BuildContext context) async { + // Gather inputs var name = inputName.text; var place = inputGeo.text; var description = inputDesc.text; @@ -152,177 +154,138 @@ class _UpdateeventImageState extends State var startDate = "${startDateFormat}T${startTimepicker.text.replaceAll('-', ':')}"; var endDate = "${endDateFormat}T${endTimepicker.text.replaceAll('-', ':')}"; - if (startDateCompare.isAfter(dateNow)) { - SharedPreferences prefs = await SharedPreferences.getInstance(); - var accessToken = prefs.getString("access_token") ?? ""; - if (accessToken.isNotEmpty) { - try { - await dotenv.load(); - final mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''; - print("place non encoded : ${place}"); - final url = - 'https://api.mapbox.com/geocoding/v5/mapbox.places/${place}.json?access_token=${mapboxAccessToken}&types=poi,address,place'; - var encoded = Uri.encodeFull(url); - print("encoded : ${encoded}"); - final response = await http.get(Uri.parse(encoded)); - - if (response.statusCode == 200) { - final data = json.decode(response.body); - print("data : ${data}"); - - if (data['features'].isNotEmpty) { - place = data['features'][0]['place_name']; - final coordinates = - data['features'][0]['geometry']['coordinates']; - final longitude = coordinates[0]; // Longitude - final latitude = coordinates[1]; // Latitude - var urlGet = Uri.parse( - "${globals.api}/events/search?item=${name}&date_event=${startDate}"); - - var responseGet = await http.get(urlGet, headers: { - HttpHeaders.cookieHeader: 'access_token=${accessToken}' - }); - if (responseGet.statusCode == 200) { - var events = jsonDecode(utf8.decode(responseGet.bodyBytes)); - print("reponse http : ${events.length}"); - if (events.length == 0) { - urlGet = Uri.parse( - "${globals.api}/events/search?min_lat=$latitude&max_lat=$latitude" - "&min_lon=$longitude&max_lon=$longitude&date_event=${startDate}"); - responseGet = await http.get(urlGet, headers: { - HttpHeaders.cookieHeader: 'access_token=${accessToken}' - }); - if (responseGet.statusCode == 200) { - events = jsonDecode(utf8.decode(responseGet.bodyBytes)); - print("reponse http : ${events.length}"); - if (events.length != 0) { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => - ItemMenu(title: events[0]["id"]))); - return; - } - } - } else { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => ItemMenu(title: events[0]["id"]))); - return; - } - } - - 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.imagePath); - 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) { - var body = json.decode(utf8.decode(res.bodyBytes)); - String imgUrl = body["data"]["url"]; - - //String credentials = "${pseudo}:${password}"; - //Codec stringToBase64 = utf8.fuse(base64); - //String encoded = stringToBase64.encode(credentials); - var urlPut = Uri.parse("${globals.api}/events"); - 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} ajoute"); - } 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; - } - showErrorDialog(context, text); - } - } else { - print("imgbb error : ${status}"); - } - } else { - showErrorDialog(context, "Aucune donnée geographique"); - } - } else { - showErrorDialog(context, "Mapbox non accessible"); - } - } catch (e) { - showErrorDialog(context, "${e}"); - } - } else { - showErrorDialog(context, "Champ vide"); - } - } else { + if (!startDateCompare.isAfter(dateNow)) { showErrorDialog(context, "Evenement non futur"); + return; } + + SharedPreferences prefs = await SharedPreferences.getInstance(); + var accessToken = prefs.getString("access_token") ?? ""; + + if (accessToken.isEmpty) { + showErrorDialog(context, "Token d'accès manquant"); + return; + } + + try { + await dotenv.load(); + final mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''; + + // Searchbox API for geocoding the place (No session token) + final searchboxUrl = + Uri.parse('https://api.mapbox.com/search/searchbox/v1/retrieve/') + .replace(queryParameters: { + 'access_token': mapboxAccessToken, + 'query': place, + 'limit': '1', + 'types': 'place,address', + }); + + final searchboxResponse = await http.get(searchboxUrl); + + if (searchboxResponse.statusCode != 200) { + showErrorDialog(context, "Erreur lors de la géocodage avec Searchbox"); + return; + } + + final searchboxData = json.decode(searchboxResponse.body); + if (searchboxData['features'].isEmpty) { + showErrorDialog(context, "Lieu introuvable"); + return; + } + + // Extract place details from the searchbox response + final firstFeature = searchboxData['features'][0]; + place = firstFeature['place_name']; + final coordinates = firstFeature['geometry']['coordinates']; + final longitude = coordinates[0]; + final latitude = coordinates[1]; + + // Check if a similar event exists + final eventsUrl = Uri.parse( + "${globals.api}/events/search?item=$name&date_event=$startDate"); + final eventsResponse = await http.get(eventsUrl, headers: { + HttpHeaders.cookieHeader: 'access_token=$accessToken', + }); + + if (eventsResponse.statusCode == 200) { + final events = json.decode(utf8.decode(eventsResponse.bodyBytes)); + if (events.isNotEmpty) { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => ItemMenu(title: events[0]["id"]), + ), + ); + return; + } + } + + // Upload image to imgbb + final imgbbUrl = Uri.parse( + 'https://api.imgbb.com/1/upload?expiration=15552000&key=${dotenv.env["IMGBB_API_KEY"]}'); + File image = File(widget.imagePath); + Uint8List imageBytes = await image.readAsBytes(); + String base64Image = base64.encode(imageBytes); + + final imgbbRequest = http.MultipartRequest('POST', imgbbUrl) + ..fields['image'] = base64Image; + final imgbbResponse = + await http.Response.fromStream(await imgbbRequest.send()); + + if (imgbbResponse.statusCode != 200) { + showErrorDialog(context, "Erreur lors de l'upload d'image"); + return; + } + + final imgbbData = json.decode(imgbbResponse.body); + final imgUrl = imgbbData['data']['url']; + + // Create or update the event + final eventUrl = Uri.parse("${globals.api}/events"); + final eventResponse = await http.put( + eventUrl, + 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, + }), + ); + + if (eventResponse.statusCode == 200 || eventResponse.statusCode == 201) { + showEventDialog(context, "Événement $name ajouté"); + } else { + handleHttpError(eventResponse.statusCode, context); + } + } catch (e) { + showErrorDialog(context, "Erreur: ${e.toString()}"); + } + } + +// Utility function to handle HTTP errors + void handleHttpError(int statusCode, BuildContext context) { + final errorMessages = { + 400: "Requête mal construite", + 403: "Utilisateur désactivé", + 404: "Utilisateur inconnu", + 406: "Mot de passe incorrect", + 410: "Token invalide", + 500: "Problème interne du serveur", + }; + showErrorDialog(context, errorMessages[statusCode] ?? "Erreur inconnue"); } void start() async { @@ -358,25 +321,32 @@ class _UpdateeventImageState extends State } Future searchSuggestions(String input) async { + var uuid = Uuid(); + String sessionToken = uuid.v4(); await dotenv.load(fileName: ".env"); // Load .env file final mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''; - final url = - 'https://api.mapbox.com/geocoding/v5/mapbox.places/${input}.json?access_token=${mapboxAccessToken}&types=poi,address,place'; - var encoded = Uri.encodeFull(url); - final response = await http.get(Uri.parse(encoded)); - print("response code suggesttion : ${response.statusCode}"); + + // Define the Searchbox API URL + final searchboxUrl = Uri.parse( + 'https://api.mapbox.com/search/searchbox/v1/suggest?q=${input}&limit=5&language=en&types=place,poi,address&session_token=${sessionToken}&access_token=${mapboxAccessToken}'); + + // Perform the request + final response = await http.get(searchboxUrl); + print("response code suggestion: ${response.statusCode}"); if (response.statusCode == 200) { final data = json.decode(response.body); - print("data suggestion : ${data}"); + print("data suggestion: ${data}"); + setState(() { - suggestions = (data['features'] as List) + // Map the results to extract name and full_address + suggestions = (data['suggestions'] as List) .map((feature) => { - 'place_name': feature['place_name'], - 'text': feature['text'], - 'geometry': feature[ - 'geometry'], // Include geometry for latitude/longitude + 'name': feature['name'], + 'full_address': feature[ + 'place_formatted'], // Adjusted to match the data structure + 'context': feature['context'], // Additional context data }) .toList(); }); @@ -426,17 +396,13 @@ class _UpdateeventImageState extends State itemCount: suggestions.length, itemBuilder: (context, index) { return ListTile( - title: Text(suggestions[index]['text']), - subtitle: Text(suggestions[index]['place_name']), + title: Text(suggestions[index]['name']), + subtitle: Text(suggestions[index]['full_address']), onTap: () async { print("suggestion tapped : ${suggestions[index]}"); - final latitude = - suggestions[index]['geometry']['coordinates'][1]; - final longitude = - suggestions[index]['geometry']['coordinates'][0]; setState(() { - geographicalZone = suggestions[index]['text']; + geographicalZone = suggestions[index]['name']; inputGeo.text = geographicalZone; suggestions.clear(); });