Add file uploads for photos and family icons
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import "dart:convert";
|
||||
import "dart:typed_data";
|
||||
import "package:http/http.dart" as http;
|
||||
import "../config/api_config.dart";
|
||||
|
||||
@@ -45,6 +46,30 @@ class ApiClient {
|
||||
return jsonDecode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> postMultipart(
|
||||
String path, {
|
||||
required String fieldName,
|
||||
required Uint8List bytes,
|
||||
required String filename,
|
||||
Map<String, String>? fields,
|
||||
}) async {
|
||||
final request = http.MultipartRequest("POST", _uri(path));
|
||||
if (fields != null) {
|
||||
request.fields.addAll(fields);
|
||||
}
|
||||
request.files.add(
|
||||
http.MultipartFile.fromBytes(
|
||||
fieldName,
|
||||
bytes,
|
||||
filename: filename,
|
||||
),
|
||||
);
|
||||
final streamed = await _client.send(request);
|
||||
final response = await http.Response.fromStream(streamed);
|
||||
_ensureSuccess(response);
|
||||
return jsonDecode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> put(
|
||||
String path,
|
||||
Map<String, dynamic> body,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import "dart:typed_data";
|
||||
|
||||
import "../config/api_config.dart";
|
||||
import "../models/family_member.dart";
|
||||
import "api_client.dart";
|
||||
@@ -23,6 +25,7 @@ class FamilyService {
|
||||
final created = FamilyMember(
|
||||
id: "family-${DateTime.now().millisecondsSinceEpoch}",
|
||||
name: member.name,
|
||||
iconUrl: member.iconUrl,
|
||||
emoji: member.emoji,
|
||||
color: member.color,
|
||||
order: member.order,
|
||||
@@ -51,6 +54,22 @@ class FamilyService {
|
||||
return FamilyMember.fromJson(data);
|
||||
}
|
||||
|
||||
Future<String> uploadFamilyIcon({
|
||||
required Uint8List bytes,
|
||||
required String filename,
|
||||
}) async {
|
||||
if (ApiConfig.useMockData) {
|
||||
return "mock://$filename";
|
||||
}
|
||||
final data = await _client.postMultipart(
|
||||
"${ApiConfig.family}/upload-icon",
|
||||
fieldName: "file",
|
||||
bytes: bytes,
|
||||
filename: filename,
|
||||
);
|
||||
return data["url"] as String? ?? "";
|
||||
}
|
||||
|
||||
Future<void> deleteFamilyMember(String id) async {
|
||||
if (ApiConfig.useMockData) {
|
||||
MockDataStore.familyMembers.removeWhere((item) => item.id == id);
|
||||
|
||||
@@ -11,28 +11,32 @@ class MockDataStore {
|
||||
const FamilyMember(
|
||||
id: "family-1",
|
||||
name: "Dad",
|
||||
emoji: ":)",
|
||||
iconUrl: "",
|
||||
emoji: "",
|
||||
color: "#0F766E",
|
||||
order: 1,
|
||||
),
|
||||
const FamilyMember(
|
||||
id: "family-2",
|
||||
name: "Mom",
|
||||
emoji: "<3",
|
||||
iconUrl: "",
|
||||
emoji: "",
|
||||
color: "#C2410C",
|
||||
order: 2,
|
||||
),
|
||||
const FamilyMember(
|
||||
id: "family-3",
|
||||
name: "Son",
|
||||
emoji: ":D",
|
||||
iconUrl: "",
|
||||
emoji: "",
|
||||
color: "#1D4ED8",
|
||||
order: 3,
|
||||
),
|
||||
const FamilyMember(
|
||||
id: "family-4",
|
||||
name: "Daughter",
|
||||
emoji: ":-)",
|
||||
iconUrl: "",
|
||||
emoji: "",
|
||||
color: "#7C3AED",
|
||||
order: 4,
|
||||
),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import "dart:typed_data";
|
||||
|
||||
import "../config/api_config.dart";
|
||||
import "../models/photo.dart";
|
||||
import "api_client.dart";
|
||||
@@ -38,6 +40,35 @@ class PhotoService {
|
||||
return Photo.fromJson(data);
|
||||
}
|
||||
|
||||
Future<Photo> uploadPhotoBytes({
|
||||
required Uint8List bytes,
|
||||
required String filename,
|
||||
String caption = "",
|
||||
bool active = true,
|
||||
}) async {
|
||||
if (ApiConfig.useMockData) {
|
||||
final created = Photo(
|
||||
id: "photo-${DateTime.now().millisecondsSinceEpoch}",
|
||||
url: "mock://$filename",
|
||||
caption: caption,
|
||||
active: active,
|
||||
);
|
||||
MockDataStore.photos.add(created);
|
||||
return created;
|
||||
}
|
||||
final data = await _client.postMultipart(
|
||||
"${ApiConfig.photos}/upload",
|
||||
fieldName: "file",
|
||||
bytes: bytes,
|
||||
filename: filename,
|
||||
fields: {
|
||||
"caption": caption,
|
||||
"active": active.toString(),
|
||||
},
|
||||
);
|
||||
return Photo.fromJson(data);
|
||||
}
|
||||
|
||||
Future<void> deletePhoto(String id) async {
|
||||
if (ApiConfig.useMockData) {
|
||||
MockDataStore.photos.removeWhere((item) => item.id == id);
|
||||
|
||||
Reference in New Issue
Block a user