anyway/frontend/lib/utils/fetch_trip.dart
Remy Moll a676af3a67
Some checks failed
Build and release debug APK / Build APK (pull_request) Failing after 3m52s
automatically save a trip when it is first created
2025-03-23 21:49:48 +01:00

126 lines
3.6 KiB
Dart

import "dart:convert";
import "dart:developer";
import "package:anyway/main.dart";
import 'package:dio/dio.dart';
import 'package:anyway/constants.dart';
import "package:anyway/utils/load_landmark_image.dart";
import "package:anyway/structs/landmark.dart";
import "package:anyway/structs/trip.dart";
import "package:anyway/structs/preferences.dart";
Dio dio = Dio(
BaseOptions(
baseUrl: API_URL_BASE,
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 120),
// also accept 500 errors, since we cannot rule out that the server is at fault. We still want to gracefully handle these errors
validateStatus: (status) => status! <= 500,
receiveDataWhenStatusError: true,
// api is notoriously slow
// headers: {
// HttpHeaders.userAgentHeader: 'dio',
// 'api': '1.0.0',
// },
contentType: Headers.jsonContentType,
responseType: ResponseType.json,
),
);
fetchTrip(
Trip trip,
UserPreferences preferences,
) async {
Map<String, dynamic> data = {
"preferences": preferences.toJson(),
"start": trip.landmarks!.first.location,
};
String dataString = jsonEncode(data);
log(dataString);
late Response response;
try {
response = await dio.post(
"/trip/new",
data: data
);
} catch (e) {
trip.updateUUID("error");
trip.updateError(e.toString());
log(e.toString());
return;
}
// handle errors
if (response.statusCode != 200) {
trip.updateUUID("error");
String errorDetail;
if (response.data.runtimeType == String) {
errorDetail = response.data;
} else {
errorDetail = response.data["detail"] ?? "Unknown error";
}
trip.updateError(errorDetail);
log(errorDetail);
// Actualy no need to throw an exception, we can just log the error and let the user retry
// throw Exception(errorDetail);
} else {
Map<String, dynamic> json = response.data;
// only fill in the trip "meta" data for now
trip.loadFromJson(json);
// now fill the trip with landmarks
// we are going to recreate ALL the landmarks from the information given by the api
trip.landmarks.remove(trip.landmarks.first);
String? nextUUID = json["first_landmark_uuid"];
while (nextUUID != null) {
var (landmark, newUUID) = await fetchLandmark(nextUUID);
trip.addLandmark(landmark);
nextUUID = newUUID;
}
log(response.data.toString());
// Also save the trip for the user's convenience
savedTrips.addTrip(trip);
}
}
patchLandmarkImage(Landmark landmark) async {
// patch the landmark to include an image from an external source
if (landmark.imageURL == null) {
String? newUrl = await getImageUrlFromName(landmark.name);
if (newUrl != null) {
landmark.imageURL = newUrl;
}
} else if (landmark.imageURL!.contains("photos.app.goo.gl")) {
// the image is a google photos link, we should get the image behind the link
String? newUrl = await getImageUrlFromGooglePhotos(landmark.imageURL!);
// also set the new url if it is null
landmark.imageURL = newUrl;
}
}
Future<(Landmark, String?)> fetchLandmark(String uuid) async {
final response = await dio.get(
"/landmark/$uuid"
);
// handle errors
if (response.statusCode != 200) {
throw Exception('Failed to load landmark');
}
if (response.data["detail"] != null) {
throw Exception(response.data["detail"]);
}
// log(response.data.toString());
Map<String, dynamic> json = response.data;
String? nextUUID = json["next_uuid"];
Landmark landmark = Landmark.fromJson(json);
patchLandmarkImage(landmark);
return (landmark, nextUUID);
}