feat(images): surface actionable upload errors for private photo pipeline

- Change uploadCarImage to throw explicit errors instead of returning null
- Handle upload exceptions in garage detail flow with clearer user feedback
- Guide users to retry with a smaller/clearer image when upload constraints fail
This commit is contained in:
Lukas Müllner 2026-03-04 11:31:37 +01:00
parent aab5c7d039
commit d287d175b5
2 changed files with 30 additions and 31 deletions

View file

@ -678,14 +678,18 @@ class GarageScreenState extends State<GarageScreen> {
showGlobalSnackBar('Uploading photo…');
final oldPath = car['user_image_url'] as String?;
final newPath = await StorageService.uploadCarImage(
file: File(xFile.path),
entryId: car['id'] as int,
oldPath: oldPath,
);
if (newPath == null) {
showGlobalSnackBar('Failed to upload photo.', isError: true);
String newPath;
try {
newPath = await StorageService.uploadCarImage(
file: File(xFile.path),
entryId: car['id'] as int,
oldPath: oldPath,
);
} catch (e) {
showGlobalSnackBar(
'Failed to upload photo. Please try a smaller/clearer image. ($e)',
isError: true,
);
return;
}

View file

@ -18,34 +18,29 @@ class StorageService {
/// Upload a car image for a specific hotwheels entry.
/// Returns the storage path on success (e.g. `uid/123.jpg`).
static Future<String?> uploadCarImage({
static Future<String> uploadCarImage({
required File file,
required int entryId,
String? oldPath,
}) async {
try {
if (oldPath != null && oldPath.isNotEmpty) {
await deleteCarImage(oldPath);
}
final userId = supabase.auth.currentUser!.id;
final path = '$userId/$entryId.jpg';
final compressed = await _compressImage(file);
await supabase.storage.from(_bucket).uploadBinary(
path,
compressed,
fileOptions: const FileOptions(
upsert: true,
contentType: 'image/jpeg',
),
);
return path;
} catch (e) {
debugPrint('StorageService.uploadCarImage error: $e');
return null;
if (oldPath != null && oldPath.isNotEmpty) {
await deleteCarImage(oldPath);
}
final userId = supabase.auth.currentUser!.id;
final path = '$userId/$entryId.jpg';
final compressed = await _compressImage(file);
await supabase.storage.from(_bucket).uploadBinary(
path,
compressed,
fileOptions: const FileOptions(
upsert: true,
contentType: 'image/jpeg',
),
);
return path;
}
/// Generates a temporary signed URL for a private image path.