From ecc9aa3abc93ee2cb91fb29361ff55b08819bc96 Mon Sep 17 00:00:00 2001 From: Joshua Burman Date: Sun, 5 Jan 2025 00:45:27 -0500 Subject: [PATCH] DRY up some search and places code --- lib/models/google_place_model.dart | 12 +++++ lib/services/apis/google_places_service.dart | 45 +++++-------------- .../generic/elements/form_search_input.dart | 40 +++++++++-------- lib/widgets/sessions/session_editor.dart | 32 ++++++++++--- 4 files changed, 72 insertions(+), 57 deletions(-) create mode 100644 lib/models/google_place_model.dart diff --git a/lib/models/google_place_model.dart b/lib/models/google_place_model.dart new file mode 100644 index 0000000..7477962 --- /dev/null +++ b/lib/models/google_place_model.dart @@ -0,0 +1,12 @@ +class GooglePlaceModel { + final String placeId; + final String description; + final String address; + final List? imageReferences; + + GooglePlaceModel( + {required this.placeId, + required this.description, + required this.address, + this.imageReferences}); +} diff --git a/lib/services/apis/google_places_service.dart b/lib/services/apis/google_places_service.dart index 9686922..21fb308 100644 --- a/lib/services/apis/google_places_service.dart +++ b/lib/services/apis/google_places_service.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart'; +import 'package:sendtrain/models/google_place_model.dart'; +import 'package:sendtrain/widgets/generic/elements/form_search_input.dart'; import 'package:uuid/uuid.dart'; class GooglePlacesService { @@ -33,11 +35,12 @@ class GooglePlacesService { if (result.isNotEmpty) { return result['places'] - .map((p) => Suggestion( - placeId: p['id'], - description: p['displayName']['text'], - address: p['formattedAddress'], - imageReferences: p['photos'])) + .map((p) => Suggestion( + GooglePlaceModel( + placeId: p['id'], + description: p['displayName']['text'], + address: p['formattedAddress'], + imageReferences: p['photos']))) .toList(); } else { return null; @@ -52,9 +55,10 @@ class GooglePlacesService { "Access-Control-Allow-Origin": "*", }; - var request = Request('GET', - Uri.parse('https://places.googleapis.com/v1/$name/media?key=$apiKey&maxWidthPx=800&skipHttpRedirect=true') - ); + var request = Request( + 'GET', + Uri.parse( + 'https://places.googleapis.com/v1/$name/media?key=$apiKey&maxWidthPx=800&skipHttpRedirect=true')); request.headers.addAll(headers); StreamedResponse response = await request.send(); @@ -72,28 +76,3 @@ class GooglePlacesService { } } } - -class Suggestion { - final String placeId; - final String description; - final String address; - final List? imageReferences; - - Suggestion( - {required this.placeId, - required this.description, - required this.address, - this.imageReferences}); - - @override - String toString() { - return 'Suggestion(description: $description, placeId: $placeId)'; - } - - Map toJson() => { - 'placeId': placeId, - 'name': description, - 'address': address, - 'imageReferences': imageReferences - }; -} diff --git a/lib/widgets/generic/elements/form_search_input.dart b/lib/widgets/generic/elements/form_search_input.dart index c1374cd..4c35e38 100644 --- a/lib/widgets/generic/elements/form_search_input.dart +++ b/lib/widgets/generic/elements/form_search_input.dart @@ -2,17 +2,28 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:sendtrain/services/apis/google_places_service.dart'; import 'package:sendtrain/services/functional/debouncer.dart'; import 'package:sendtrain/widgets/generic/elements/form_text_input.dart'; +class Suggestion { + T type; + + Suggestion(this.type); +} + class FormSearchInput extends StatefulWidget { const FormSearchInput( - {super.key, required this.sessionController, required this.service, this.requestCallback, this.optionalPayload}); + {super.key, + required this.sessionController, + required this.service, + this.title, + this.callback, + this.optionalPayload}); + final String? title; final TextEditingController sessionController; final dynamic service; - final Function? requestCallback; + final Function? callback; final dynamic optionalPayload; @override @@ -23,7 +34,7 @@ class _FormSearchInputState extends State { String? _currentQuery; late final service = widget.service; - late final requestCallback = widget.requestCallback; + late final callback = widget.callback; // The most recent suggestions received from the API. late Iterable _lastOptions = []; late final Debouncer debouncer; @@ -68,7 +79,7 @@ class _FormSearchInputState extends State { builder: (BuildContext context, SearchController controller) { return FormTextInput( controller: widget.sessionController, - title: 'Location (optional)', + title: widget.title ?? "", icon: Icon(Icons.search_rounded), maxLines: 2, requiresValidation: false, @@ -84,24 +95,15 @@ class _FormSearchInputState extends State { } _lastOptions = List.generate(options.length, (int index) { final Suggestion item = options[index]; + final dynamic content = item.type; return ListTile( - title: Text(item.description), + title: Text(content.description), onTap: () async { - // widget.optionalPayload = service.fetchPhoto(json.decode(item.image)); - if (item.imageReferences != null) { - // get a random photo item from the returned result - Map photo = item.imageReferences![ - Random().nextInt(item.imageReferences!.length)]; - - await service.fetchPhoto(photo['name']).then((photoMap) { - widget.optionalPayload.photoUri = photoMap['photoUri']; - }); + if (callback != null) { + callback!(content, service); } - widget.optionalPayload.address = item.address; - widget.sessionController.text = item.description; - service.finish(); - controller.closeView(item.description); + controller.closeView(null); }, ); }); diff --git a/lib/widgets/sessions/session_editor.dart b/lib/widgets/sessions/session_editor.dart index d58a960..e313605 100644 --- a/lib/widgets/sessions/session_editor.dart +++ b/lib/widgets/sessions/session_editor.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:math'; import 'package:drift/drift.dart' hide Column; import 'package:file_picker/file_picker.dart'; @@ -181,9 +182,27 @@ class _SessionEditorState extends State { }); }), FormSearchInput( + title: 'Location (optional)', sessionController: sessionCreateController['address']!, service: GooglePlacesService(), - optionalPayload: sessionPayload), + callback: (content, service) async { + if (content.imageReferences != null) { + // get a random photo item from the returned result + Map photo = content.imageReferences![ + Random().nextInt(content.imageReferences!.length)]; + + await service + .fetchPhoto(photo['name']) + .then((photoMap) { + sessionPayload.photoUri = photoMap['photoUri']; + }); + } + + sessionPayload.address = content.address; + sessionCreateController['address']!.text = + content.description; + service.finish(); + }), Padding( padding: EdgeInsets.only(top: 10, bottom: 10), child: TextFormField( @@ -248,11 +267,15 @@ class _SessionEditorState extends State { i++) { PlatformFile file = sessionPayload.files![i]; - String? type = lookupMimeType(file.path!)!.split('/').first; + String? type = + lookupMimeType(file.path!)! + .split('/') + .first; Uint8List fileBytes = await file.xFile.readAsBytes(); - MediaType mediaType = MediaType.localImage; + MediaType mediaType = + MediaType.localImage; if (type == "video") { mediaType = MediaType.localVideo; } @@ -282,8 +305,7 @@ class _SessionEditorState extends State { _formKey.currentContext!, 'Submit'); if (widget.callback != null) { - await widget - .callback!(); + await widget.callback!(); } }) }