location image search and population for new session

This commit is contained in:
Joshua Burman
2024-12-30 01:54:51 -05:00
parent afe633e697
commit 5f628d6b48
11 changed files with 337 additions and 168 deletions

View File

@ -0,0 +1,99 @@
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart';
import 'package:uuid/uuid.dart';
class GooglePlacesService {
final sessionToken = Uuid().v4();
final apiKey = "AIzaSyBCjMCEAyyNVpsnVYvZj6VL1mmB98Vd6AE";
final client = Client();
void finish() {
client.close();
}
Future<List<Suggestion>?> fetchSuggestions(String input, String lang) async {
var headers = {
'Content-Type': 'application/json',
'X-Goog-Api-Key': apiKey,
"Access-Control-Allow-Origin": "*",
'X-Goog-FieldMask':
'places.displayName,places.id,places.formattedAddress,places.photos'
};
var request = Request('POST',
Uri.parse('https://places.googleapis.com/v1/places:searchText'));
request.body = json.encode({"textQuery": input});
request.headers.addAll(headers);
StreamedResponse response = await request.send();
if (response.statusCode == 200) {
final result = json.decode(await response.stream.bytesToString());
if (result.isNotEmpty) {
return result['places']
.map<Suggestion>((p) => Suggestion(
placeId: p['id'],
description: p['displayName']['text'],
address: p['formattedAddress'],
imageReferences: p['photos']))
.toList();
} else {
return null;
}
} else {
throw Exception(response.reasonPhrase);
}
}
Future fetchPhoto(String name) async {
var headers = {
"Access-Control-Allow-Origin": "*",
};
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();
if (response.statusCode == 200) {
final result = json.decode(await response.stream.bytesToString());
if (result.isNotEmpty) {
return result;
} else {
return null;
}
} else {
throw Exception(response.reasonPhrase);
}
}
}
class Suggestion {
final String placeId;
final String description;
final String address;
final List<dynamic>? 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
};
}

View File

@ -0,0 +1,68 @@
import 'dart:async';
typedef _Debounceable<S, T> = Future<S?> Function(T parameter);
class Debouncer {
Debouncer(this._duration, this._callback);
final Duration _duration;
final dynamic _callback;
late final _Debounceable<dynamic, String> _debouncedSearch = _debounce<dynamic, String>(_callback);
/// Returns a new function that is a debounced version of the given function.
///
/// This means that the original function will be called only after no calls
/// have been made for the given Duration.
_Debounceable<S, T> _debounce<S, T>(_Debounceable<S?, T> function) {
_DebounceTimer? debounceTimer;
return (T parameter) async {
if (debounceTimer != null && !debounceTimer!.isCompleted) {
debounceTimer!.cancel();
}
debounceTimer = _DebounceTimer(_duration);
try {
await debounceTimer!.future;
} on _CancelException {
return null;
}
return function(parameter);
};
}
process(data) {
return _debouncedSearch(data);
}
}
// A wrapper around Timer used for debouncing.
class _DebounceTimer {
final Duration debounceDuration;
_DebounceTimer(
this.debounceDuration
) {
_timer = Timer(debounceDuration, _onComplete);
}
late final Timer _timer;
final Completer<void> _completer = Completer<void>();
void _onComplete() {
_completer.complete();
}
Future<void> get future => _completer.future;
bool get isCompleted => _completer.isCompleted;
void cancel() {
_timer.cancel();
_completer.completeError(const _CancelException());
}
}
// An exception indicating that the timer was canceled.
class _CancelException implements Exception {
const _CancelException();
}