From 908d94c269a2eb604cde3ebd8c3a1a08937596b2 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Sun, 19 Jan 2025 21:49:09 +0100 Subject: [PATCH 1/4] fix import --- covas_mobile/lib/pages/ItemMenu.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/covas_mobile/lib/pages/ItemMenu.dart b/covas_mobile/lib/pages/ItemMenu.dart index 145353f..381313f 100644 --- a/covas_mobile/lib/pages/ItemMenu.dart +++ b/covas_mobile/lib/pages/ItemMenu.dart @@ -14,7 +14,7 @@ import 'package:intl/date_symbol_data_local.dart'; import '../variable/globals.dart' as globals; import '../classes/events.dart'; -IMPORT '../classes/MyDrawer.dart'; +import '../classes/MyDrawer.dart'; import 'ListItemMenu.dart'; import 'MapboxPages.dart'; From 06e26240ab47ff2c24c7b981f201d41e07df6354 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Sun, 19 Jan 2025 22:26:45 +0100 Subject: [PATCH 2/4] refactor itemmenu --- covas_mobile/lib/pages/ItemMenu.dart | 148 +++++++++------------------ 1 file changed, 51 insertions(+), 97 deletions(-) diff --git a/covas_mobile/lib/pages/ItemMenu.dart b/covas_mobile/lib/pages/ItemMenu.dart index 381313f..3e40bb4 100644 --- a/covas_mobile/lib/pages/ItemMenu.dart +++ b/covas_mobile/lib/pages/ItemMenu.dart @@ -89,107 +89,61 @@ class _ItemMenuState extends State with ShowAlertDialog { } Future _getEventInfos() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - var accessToken = prefs.getString("access_token") ?? ""; - String formerName = ""; - String formerDate = ""; - String formerMap = ""; - String formerImage = ""; - String formerDesc = ""; - List formerTags = []; - List formerOrga = []; + final prefs = await SharedPreferences.getInstance(); + final accessToken = prefs.getString("access_token") ?? ""; - if (accessToken.isNotEmpty) { - var urlGet = Uri.parse("${globals.api}/events/${widget.title}"); - - var responseGet = await http.get(urlGet, - headers: {HttpHeaders.cookieHeader: 'access_token=${accessToken}'}); - stderr.writeln('Response Get status: ${responseGet.statusCode}'); - if (responseGet.statusCode == 200) { - stderr.writeln('Username : ${responseGet.body}'); - events = - Events.fromJson(jsonDecode(utf8.decode(responseGet.bodyBytes))); - id = events!.id ?? ""; - formerName = events!.name ?? ""; - formerMap = "${events!.place}" ?? ""; - formerDesc = events!.description ?? ""; - - formerTags = List.from(events!.tags as List); - formerOrga = List.from(events!.organizers as List); - final startDate = - DateTime.parse(events!.startDate ?? DateTime.now().toString()); - final date = DateFormat.yMd().format(startDate); - final time = DateFormat.Hm().format(startDate); - - final endDate = - DateTime.parse(events!.endDate ?? DateTime.now().toString()); - - final dateE = DateFormat.yMd().format(endDate); - final timeE = DateFormat.Hm().format(endDate); - if (events!.imgUrl != null) { - formerImage = events!.imgUrl ?? ""; - } - - formerDate = "${date} ${time} à ${dateE} ${timeE}"; - } else { - var text = ""; - switch (responseGet.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 = "Vous n'avez pas l'autorisation de faire cette action"; - } - 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 { + if (accessToken.isEmpty) { showAlertDialog(context, "Erreur serveur", "Cache invalide"); + return; } - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - eventName = formerName; - eventStartDate = formerDate; - organizers = formerOrga; - place = formerMap; - imgUrl = formerImage; - eventDescription = formerDesc; - tags = formerTags; - }); + final urlGet = Uri.parse("${globals.api}/events/${widget.title}"); + final responseGet = await http.get( + urlGet, + headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'}, + ); + + stderr.writeln('Response Get status: ${responseGet.statusCode}'); + + if (responseGet.statusCode == 200) { + final responseBody = utf8.decode(responseGet.bodyBytes); + stderr.writeln('Username : $responseBody'); + + final event = Events.fromJson(jsonDecode(responseBody)); + + final startDate = + DateTime.parse(event.startDate ?? DateTime.now().toString()); + final endDate = + DateTime.parse(event.endDate ?? DateTime.now().toString()); + + final formattedStartDate = + "${DateFormat.yMd().format(startDate)} ${DateFormat.Hm().format(startDate)}"; + final formattedEndDate = + "${DateFormat.yMd().format(endDate)} ${DateFormat.Hm().format(endDate)}"; + + setState(() { + eventName = event.name ?? ""; + eventStartDate = "$formattedStartDate à $formattedEndDate"; + organizers = List.from(event.organizers ?? []); + place = event.place ?? ""; + imgUrl = event.imgUrl ?? ""; + eventDescription = event.description ?? ""; + tags = List.from(event.tags ?? []); + }); + } else { + final errorMessages = { + 400: "Requête mal construite", + 406: "Mot de passe incorrect", + 404: "Utilisateur inconnu", + 403: "Vous n'avez pas l'autorisation de faire cette action", + 410: "Token invalide", + 500: "Problème interne du serveur", + }; + + final errorMessage = errorMessages[responseGet.statusCode] ?? + "Problème d'authentification inconnu"; + showAlertDialog(context, "Erreur serveur", errorMessage); + } } final _formKey = GlobalKey(); From 56ae77137cc1f72d0afa60542345e0372ff4784f Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Sun, 19 Jan 2025 22:41:13 +0100 Subject: [PATCH 3/4] refactor listitemmenu --- covas_mobile/lib/pages/ListItemMenu.dart | 380 ++++++++++------------- 1 file changed, 158 insertions(+), 222 deletions(-) diff --git a/covas_mobile/lib/pages/ListItemMenu.dart b/covas_mobile/lib/pages/ListItemMenu.dart index 46dc868..dd7df3a 100644 --- a/covas_mobile/lib/pages/ListItemMenu.dart +++ b/covas_mobile/lib/pages/ListItemMenu.dart @@ -480,65 +480,33 @@ class _MyHomePageState extends State { ); } - Padding _buildGeographicalZoneSearchField() { + Widget _buildSearchField({ + required TextEditingController controller, + required String labelText, + required Function(String) onChanged, + required Function() onClear, + required List> suggestions, + required Function(Map) onSuggestionTap, + }) { return Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ TextField( - controller: inputGeo, + controller: controller, decoration: InputDecoration( - labelText: 'Search by geographical zone', + labelText: labelText, border: OutlineInputBorder(), - suffixIcon: inputGeo.text.isEmpty + suffixIcon: controller.text.isEmpty ? null : IconButton( icon: const Icon(Icons.clear), - 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 - suggestionsGeo.clear(); - showArrow = true; - showInputSearch = - true; // Optionally clear suggestions - /// Clear the filtered posts - showInputTag = true; - }); - fetchPostsByLocation(); - }, + onPressed: () => onClear(), ), ), - onChanged: (value) async { - if (value.isNotEmpty) { - setState(() { - geographicalZone = value; - searchSuggestionsGeo(value); - }); - } else { - 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 - suggestionsGeo.clear(); // Optionally clear suggestions - showArrow = true; - showInputSearch = true; - showInputTag = true; - - /// Clear the filted posts - }); - fetchPostsByLocation(); - } - }, + onChanged: onChanged, ), - if (suggestionsGeo.isNotEmpty) + if (suggestions.isNotEmpty) Container( height: 200, decoration: BoxDecoration( @@ -547,181 +515,13 @@ class _MyHomePageState extends State { ), child: ListView.builder( shrinkWrap: true, - itemCount: suggestionsGeo.length, + itemCount: suggestions.length, itemBuilder: (context, index) { + final suggestion = suggestions[index]; return ListTile( - title: Text(suggestionsGeo[index]['place_name']), - onTap: () async { - final latitude = - suggestionsGeo[index]['geometry']['coordinates'][1]; - final longitude = - suggestionsGeo[index]['geometry']['coordinates'][0]; - - setState(() { - geographicalZone = suggestionsGeo[index]['place_name']; - inputGeo.text = geographicalZone; - suggestionsGeo.clear(); - showArrow = true; - showInputSearch = true; - showInputTag = true; - }); - SharedPreferences prefs = - await SharedPreferences.getInstance(); - prefs.setDouble("city_lat", latitude); - prefs.setDouble("city_long", longitude); - await fetchPostsByLocation(); - }, - ); - }, - ), - ), - ], - ), - ); - } - - Padding _buildTagsField() { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - TextField( - controller: inputTags, - decoration: InputDecoration( - labelText: 'Search by tags', - border: OutlineInputBorder(), - suffixIcon: inputTags.text.isEmpty - ? null - : IconButton( - icon: const Icon(Icons.clear), - onPressed: () { - setState(() { - inputTags.clear(); - }); - fetchPostsByLocation(); - }, - ), - ), - onChanged: (value) { - if (value.isNotEmpty) { - setState(() { - itemTags = value; - searchSuggestionsByTag(value); - }); - } else { - setState(() { - inputTags.clear(); - showArrow = true; - showInputSearch = true; - showInputGeo = true; // Optionally clear suggestions - itemTags = ''; // Clear the text field - - /// Clear the filted posts - }); - 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: suggestionsTags.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; - }); - await fetchPostsByLocation(); - }, - ); - }, - ), - ), - ], - ), - ); - } - - Padding _buildItemZoneSearchField() { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - TextField( - controller: inputItem, - decoration: InputDecoration( - labelText: 'Search by item', - border: OutlineInputBorder(), - suffixIcon: inputItem.text.isEmpty - ? null - : IconButton( - icon: const Icon(Icons.clear), - onPressed: () { - setState(() { - inputItem.clear(); - itemName = ''; // Reset the geographical zone state - suggestionsItem.clear(); - showDateFields = true; - showArrow = true; - }); - fetchPostsByLocation(); - }, - ), - ), - onChanged: (value) { - if (value.isNotEmpty) { - setState(() { - itemName = value; - searchSuggestionsByItem(value); - }); - } else { - setState(() { - showDateFields = true; - showArrow = true; // Optionally clear suggestions - inputItem.clear(); // Clear the text field - itemName = ''; // Reset the geographical zone state - suggestionsItem.clear(); - - /// Clear the filted posts - }); - fetchPostsByLocation(); - } - }), - if (suggestionsItem.isNotEmpty) - Container( - height: 200, - decoration: BoxDecoration( - border: Border.all(color: Colors.blue), - borderRadius: BorderRadius.circular(8), - ), - child: ListView.builder( - shrinkWrap: true, - itemCount: suggestionsItem.length, - itemBuilder: (context, index) { - return ListTile( - title: Text(suggestionsItem[index]['name']), - onTap: () async { - setState(() { - itemName = suggestionsItem[index]['name']; - inputItem.text = itemName; - suggestionsItem.clear(); - showDateFields = true; - showArrow = true; - }); - await fetchPostsByLocation(); - }, + title: Text( + suggestion['name'] ?? suggestion['place_name'] ?? ''), + onTap: () => onSuggestionTap(suggestion), ); }, ), @@ -747,8 +547,89 @@ class _MyHomePageState extends State { drawer: MyDrawer(), body: Column( children: [ - if (showInputSearch) _buildItemZoneSearchField(), - if ((showDateFields) && (showInputTag)) _buildTagsField(), + if (showInputSearch) + _buildSearchField( + controller: inputItem, + labelText: 'Search by item', + onChanged: (value) { + if (value.isNotEmpty) { + setState(() { + itemName = value; + searchSuggestionsByItem(value); + }); + } else { + setState(() { + inputItem.clear(); + itemName = ''; + suggestionsItem.clear(); + showDateFields = true; + showArrow = true; + }); + fetchPostsByLocation(); + } + }, + onClear: () { + setState(() { + inputItem.clear(); + itemName = ''; + suggestionsItem.clear(); + showDateFields = true; + showArrow = true; + }); + fetchPostsByLocation(); + }, + suggestions: suggestionsItem, + onSuggestionTap: (suggestion) async { + setState(() { + itemName = suggestion['name']; + inputItem.text = itemName; + suggestionsItem.clear(); + showDateFields = true; + showArrow = true; + }); + await fetchPostsByLocation(); + }, + ), + if ((showDateFields) && (showInputTag)) + _buildSearchField( + controller: inputTags, + labelText: 'Search by tags', + onChanged: (value) { + if (value.isNotEmpty) { + setState(() { + itemTags = value; + searchSuggestionsByTag(value); + }); + } else { + setState(() { + inputTags.clear(); + showArrow = true; + showInputSearch = true; + showInputGeo = true; + itemTags = ''; + }); + fetchPostsByLocation(); + } + }, + onClear: () { + setState(() { + inputTags.clear(); + }); + fetchPostsByLocation(); + }, + suggestions: suggestionsTags, + onSuggestionTap: (suggestion) async { + setState(() { + itemTags = suggestion['name']; + inputTags.text = itemTags; + suggestionsTags.clear(); + showArrow = true; + showInputSearch = true; + showInputGeo = true; + }); + await fetchPostsByLocation(); + }, + ), if ((showDateFields) && (showArrow)) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -757,7 +638,62 @@ class _MyHomePageState extends State { Flexible(child: _buildDateField("end")) ]), if ((showDateFields) && (showInputGeo)) - _buildGeographicalZoneSearchField(), + _buildSearchField( + controller: inputGeo, + labelText: 'Search by geographical zone', + onChanged: (value) async { + if (value.isNotEmpty) { + setState(() { + geographicalZone = value; + searchSuggestionsGeo(value); + }); + } else { + final prefs = await SharedPreferences.getInstance(); + prefs.remove("city_lat"); + prefs.remove("city_long"); + setState(() { + inputGeo.clear(); + geographicalZone = ''; + suggestionsGeo.clear(); + showArrow = true; + showInputSearch = true; + showInputTag = true; + }); + fetchPostsByLocation(); + } + }, + onClear: () async { + final prefs = await SharedPreferences.getInstance(); + prefs.remove("city_lat"); + prefs.remove("city_long"); + setState(() { + inputGeo.clear(); + geographicalZone = ''; + suggestionsGeo.clear(); + showArrow = true; + showInputSearch = true; + showInputTag = true; + }); + fetchPostsByLocation(); + }, + suggestions: suggestionsGeo, + onSuggestionTap: (suggestion) async { + final latitude = suggestion['geometry']['coordinates'][1]; + final longitude = suggestion['geometry']['coordinates'][0]; + setState(() { + geographicalZone = suggestion['place_name']; + inputGeo.text = geographicalZone; + suggestionsGeo.clear(); + showArrow = true; + showInputSearch = true; + showInputTag = true; + }); + final prefs = await SharedPreferences.getInstance(); + prefs.setDouble("city_lat", latitude); + prefs.setDouble("city_long", longitude); + await fetchPostsByLocation(); + }, + ), if (showArrow) IconButton( onPressed: () { From b7fe1da68154e44e1a32126684af0c50b4ea7657 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Mon, 20 Jan 2025 21:42:14 +0100 Subject: [PATCH 4/4] refactor code --- covas_mobile/lib/pages/ListItemMenu.dart | 80 +++++++++++------------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/covas_mobile/lib/pages/ListItemMenu.dart b/covas_mobile/lib/pages/ListItemMenu.dart index dd7df3a..92bd94c 100644 --- a/covas_mobile/lib/pages/ListItemMenu.dart +++ b/covas_mobile/lib/pages/ListItemMenu.dart @@ -281,62 +281,58 @@ class _MyHomePageState extends State { } Future getUrlForEvents() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - double latitude = prefs.getDouble("city_lat") ?? 0.0; - double longitude = prefs.getDouble("city_long") ?? 0.0; - String stringParameter = ""; + final prefs = await SharedPreferences.getInstance(); + final latitude = prefs.getDouble("city_lat") ?? 0.0; + final longitude = prefs.getDouble("city_long") ?? 0.0; + + final radiusInKm = 50; String endpoint = "events"; - if ((latitude != 0.0) && (longitude != 0.0)) { - // Calculate the boundaries - double radiusInKm = 50; - double latDistance = radiusInKm / 111.0; - double lonDistance = radiusInKm / (111.0 * cos(latitude * pi / 180)); + String queryParameters = ""; - double minLat = latitude - latDistance; - double maxLat = latitude + latDistance; - double minLon = longitude - lonDistance; - double maxLon = longitude + lonDistance; - endpoint = "events/search"; + if (latitude != 0.0 && longitude != 0.0) { + final latDistance = radiusInKm / 111.0; + final lonDistance = radiusInKm / (111.0 * cos(latitude * pi / 180)); + + final minLat = latitude - latDistance; + final maxLat = latitude + latDistance; + final minLon = longitude - lonDistance; + final maxLon = longitude + lonDistance; - stringParameter = "min_lat=$minLat&max_lat=$maxLat" - "&min_lon=$minLon&max_lon=$maxLon"; - } - DateTime currentDate = DateTime.now(); - String dateParameter = "current_datetime=${currentDate.toString()}"; - if (startDatepicker.text.isNotEmpty) { - var date = DateTime.parse(formatDate(startDatepicker.text)); - dateParameter = "date_event=" + date.toString(); endpoint = "events/search"; + queryParameters = + "min_lat=$minLat&max_lat=$maxLat&min_lon=$minLon&max_lon=$maxLon"; } - if (endDatepicker.text.isNotEmpty) { - var date = DateTime.parse(formatDate(endDatepicker.text)); - dateParameter = "date_event=" + date.toString(); - endpoint = "events/search"; - } - if ((startDatepicker.text.isNotEmpty) && (endDatepicker.text.isNotEmpty)) { - var startDate = DateTime.parse(formatDate(startDatepicker.text)); - var endDate = DateTime.parse(formatDate(endDatepicker.text)); - dateParameter = "&start_date=" + - startDate.toString() + - "&end_date=" + - endDate.toString(); + + final currentDate = DateTime.now(); + String dateParameter = "current_datetime=${currentDate.toIso8601String()}"; + + if (startDatepicker.text.isNotEmpty || endDatepicker.text.isNotEmpty) { endpoint = "events/search"; + if (startDatepicker.text.isNotEmpty) { + final startDate = DateTime.parse(formatDate(startDatepicker.text)); + dateParameter = "start_date=${startDate.toIso8601String()}"; + } + if (endDatepicker.text.isNotEmpty) { + final endDate = DateTime.parse(formatDate(endDatepicker.text)); + dateParameter += "&end_date=${endDate.toIso8601String()}"; + } } if (inputItem.text.isNotEmpty) { - stringParameter = stringParameter + "&item=${inputItem.text}"; - endpoint = "events/search"; + queryParameters += "&item=${inputItem.text}"; } + if (inputTags.text.isNotEmpty) { - stringParameter = stringParameter + "&tags=${inputTags.text}"; - endpoint = "events/search"; + queryParameters += "&tags=${inputTags.text}"; } - if (stringParameter.isNotEmpty) { - stringParameter = "$stringParameter&$dateParameter"; + + if (queryParameters.isNotEmpty) { + queryParameters = "$queryParameters&$dateParameter"; } else { - stringParameter = dateParameter; + queryParameters = dateParameter; } - return Uri.parse("${globals.api}/${endpoint}?${stringParameter}"); + + return Uri.parse("${globals.api}/$endpoint?$queryParameters"); } Future searchSuggestionsByItem(String input) async {