feat(contribute): upload event images to OneDrive before submission
- ApiClient.uploadFile() — multipart POST to /v1/upload/file (60s timeout)
- ApiEndpoints.uploadFile — points to Node.js upload endpoint
- GamificationService.submitContribution() now uploads each picked image
to OneDrive via the server upload pipeline, then passes the returned
{ fileId, url, ... } objects as `media` in the submission body
(replaces broken behaviour of sending local device paths as strings)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -99,6 +99,42 @@ class ApiClient {
|
||||
return _handleResponse(url, response, finalBody);
|
||||
}
|
||||
|
||||
/// Upload a single file as multipart/form-data.
|
||||
///
|
||||
/// Returns the `file` object from the server response:
|
||||
/// `{ fileId, url, name, type, mimeType, size, backend }`
|
||||
Future<Map<String, dynamic>> uploadFile(String url, String filePath) async {
|
||||
final request = http.MultipartRequest('POST', Uri.parse(url));
|
||||
request.files.add(await http.MultipartFile.fromPath('file', filePath));
|
||||
|
||||
late http.StreamedResponse streamed;
|
||||
try {
|
||||
streamed = await request.send().timeout(const Duration(seconds: 60));
|
||||
} catch (e) {
|
||||
throw Exception('Upload network error: $e');
|
||||
}
|
||||
|
||||
final body = await streamed.stream.bytesToString();
|
||||
dynamic decoded;
|
||||
try {
|
||||
decoded = jsonDecode(body);
|
||||
} catch (_) {
|
||||
throw Exception('Upload response parse error');
|
||||
}
|
||||
|
||||
if (streamed.statusCode >= 200 && streamed.statusCode < 300) {
|
||||
if (decoded is Map<String, dynamic> && decoded['file'] is Map) {
|
||||
return Map<String, dynamic>.from(decoded['file'] as Map);
|
||||
}
|
||||
return decoded is Map<String, dynamic> ? decoded : {};
|
||||
}
|
||||
|
||||
final msg = (decoded is Map && decoded['message'] is String)
|
||||
? decoded['message'] as String
|
||||
: 'Upload failed (${streamed.statusCode})';
|
||||
throw Exception(msg);
|
||||
}
|
||||
|
||||
/// GET request
|
||||
///
|
||||
/// - If requiresAuth==true, token & username will be attached as query parameters.
|
||||
|
||||
Reference in New Issue
Block a user