From cf00cdb01212cc8e0f0428262a4e3b80e4c299e9 Mon Sep 17 00:00:00 2001 From: jwr1 <47087725+jwr1@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:56:41 -0500 Subject: [PATCH 1/2] Refactor ServerClient to handle MultipartRequests --- lib/src/api/client.dart | 123 +++++++++++++++++---------------- lib/src/api/comments.dart | 31 ++++----- lib/src/api/images.dart | 58 +++++++++------- lib/src/api/microblogs.dart | 31 ++++----- lib/src/api/threads.dart | 120 ++++++++++++++++---------------- lib/src/api/users.dart | 132 ++++++++++++++++++------------------ 6 files changed, 248 insertions(+), 247 deletions(-) diff --git a/lib/src/api/client.dart b/lib/src/api/client.dart index 62233e65..b0a8a1a3 100644 --- a/lib/src/api/client.dart +++ b/lib/src/api/client.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; @@ -35,72 +36,68 @@ class ServerClient { Future get( String path, { - Map? headers, - JsonMap? body, Map? queryParams, - }) => - send('GET', path, headers: headers, body: body, queryParams: queryParams); + JsonMap? body, + }) => _send('GET', path, queryParams: queryParams, body: body); Future post( String path, { - Map? headers, - JsonMap? body, Map? queryParams, - }) => send( - 'POST', - path, - headers: headers, - body: body, - queryParams: queryParams, - ); + JsonMap? body, + }) => _send('POST', path, queryParams: queryParams, body: body); Future put( String path, { - Map? headers, - JsonMap? body, Map? queryParams, - }) => - send('PUT', path, headers: headers, body: body, queryParams: queryParams); + JsonMap? body, + }) => _send('PUT', path, queryParams: queryParams, body: body); Future delete( String path, { - Map? headers, - JsonMap? body, Map? queryParams, - }) => send( - 'DELETE', - path, - headers: headers, - body: body, - queryParams: queryParams, - ); + JsonMap? body, + }) => _send('DELETE', path, queryParams: queryParams, body: body); - Future send( + Future _send( String method, String path, { - Map? headers, - JsonMap? body, Map? queryParams, + JsonMap? body, }) async { - final request = http.Request( - method, - Uri.https( - domain, - software.apiPathPrefix + path, - queryParams == null ? null : _normalizeQueryParams(queryParams), - ), - ); + final request = http.Request(method, _uri(path, queryParams: queryParams)); if (body != null) { request.body = jsonEncode(body); request.headers['Content-Type'] = 'application/json'; } - if (headers != null) request.headers.addAll(headers); - return sendRequest(request); + return _sendRequest(request); + } + + Future postMultipart( + String path, { + Map? queryParams, + FutureOr Function(http.MultipartRequest request)? builder, + }) => + _sendMultipart('POST', path, queryParams: queryParams, builder: builder); + + Future _sendMultipart( + String method, + String path, { + Map? queryParams, + FutureOr Function(http.MultipartRequest request)? builder, + }) async { + final request = http.MultipartRequest( + method, + _uri(path, queryParams: queryParams), + ); + + await builder?.call(request); + + return _sendRequest(request); } - Future sendRequest(http.BaseRequest request) async { + Future _sendRequest(http.BaseRequest request) async { final response = await http.Response.fromStream( await httpClient.send(request), ); @@ -110,6 +107,12 @@ class ServerClient { return response; } + Uri _uri(String path, {Map? queryParams}) => Uri.https( + domain, + software.apiPathPrefix + path, + queryParams == null ? null : _normalizeQueryParams(queryParams), + ); + /// Remove null and empty values. Map _normalizeQueryParams(Map queryParams) => Map.from( @@ -120,26 +123,6 @@ class ServerClient { ), ); - /// Throws an error if [response] is not successful. - static void checkResponseSuccess(Uri url, http.Response response) { - if (response.statusCode < 400) return; - if (response.statusCode == 401) { - throw RestrictedAuthException(response.body, url); - } - - var message = 'Request failed with status ${response.statusCode}'; - - if (response.reasonPhrase != null) { - message = '$message: ${response.reasonPhrase}'; - } - - if (response.body.isNotEmpty) { - message = '$message: ${response.body}'; - } - - throw http.ClientException(message, url); - } - Future> languageCodeIdPairs() async { if (_langCodeIdPairs == null) { List allLanguages; @@ -180,6 +163,26 @@ class ServerClient { } return null; } + + /// Throws an error if [response] is not successful. + static void checkResponseSuccess(Uri url, http.Response response) { + if (response.statusCode < 400) return; + if (response.statusCode == 401) { + throw RestrictedAuthException(response.body, url); + } + + var message = 'Request failed with status ${response.statusCode}'; + + if (response.reasonPhrase != null) { + message = '$message: ${response.reasonPhrase}'; + } + + if (response.body.isNotEmpty) { + message = '$message: ${response.body}'; + } + + throw http.ClientException(message, url); + } } extension BodyJson on http.Response { diff --git a/lib/src/api/comments.dart b/lib/src/api/comments.dart index 179468af..e314e99e 100644 --- a/lib/src/api/comments.dart +++ b/lib/src/api/comments.dart @@ -299,23 +299,22 @@ class APIComments { final path = '/${_postTypeMbin[postType]}/$postId/comments${parentCommentId != null ? '/$parentCommentId/reply' : ''}/image'; - final request = http.MultipartRequest( - 'POST', - Uri.https(client.domain, client.software.apiPathPrefix + path), - ); - final file = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), + final response = await client.postMultipart( + path, + builder: (request) async { + final file = http.MultipartFile.fromBytes( + 'uploadImage', + await image.readAsBytes(), + filename: basename(image.path), + contentType: MediaType.parse(lookupMimeType(image.path)!), + ); + request.files.add(file); + request.fields['body'] = body; + request.fields['lang'] = lang; + request.fields['isAdult'] = isAdult.toString(); + request.fields['alt'] = alt ?? ''; + }, ); - request.files.add(file); - request.fields['body'] = body; - request.fields['lang'] = lang; - request.fields['isAdult'] = isAdult.toString(); - request.fields['alt'] = alt ?? ''; - - final response = await client.sendRequest(request); return CommentModel.fromMbin(response.bodyJson); } diff --git a/lib/src/api/images.dart b/lib/src/api/images.dart index 40def466..cd40d6f5 100644 --- a/lib/src/api/images.dart +++ b/lib/src/api/images.dart @@ -3,6 +3,7 @@ import 'package:http_parser/http_parser.dart'; import 'package:image_picker/image_picker.dart'; import 'package:interstellar/src/api/client.dart'; import 'package:interstellar/src/controller/server.dart'; +import 'package:interstellar/src/utils/globals.dart'; import 'package:interstellar/src/utils/utils.dart'; import 'package:mime/mime.dart'; import 'package:path/path.dart'; @@ -35,19 +36,18 @@ class APIImages { case ServerSoftware.lemmy: const path = '/pictrs/image'; - final request = http.MultipartRequest( - 'POST', - Uri.https(client.domain, path), + final response = await client.postMultipart( + path, + builder: (request) async { + final file = http.MultipartFile.fromBytes( + 'images[]', + await image.readAsBytes(), + filename: basename(image.path), + contentType: MediaType.parse(lookupMimeType(image.path)!), + ); + request.files.add(file); + }, ); - final file = http.MultipartFile.fromBytes( - 'images[]', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(file); - - final response = await client.sendRequest(request); final imageName = ((response.bodyJson['files']! as List).first @@ -59,19 +59,18 @@ class APIImages { case ServerSoftware.piefed: const path = '/upload/image'; - final request = http.MultipartRequest( - 'POST', - Uri.https(client.domain, client.software.apiPathPrefix + path), + final response = await client.postMultipart( + path, + builder: (request) async { + final file = http.MultipartFile.fromBytes( + 'file', + await image.readAsBytes(), + filename: basename(image.path), + contentType: MediaType.parse(lookupMimeType(image.path)!), + ); + request.files.add(file); + }, ); - final file = http.MultipartFile.fromBytes( - 'file', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(file); - - final response = await client.sendRequest(request); return response.bodyJson['url']! as String; } @@ -89,7 +88,11 @@ class APIImages { request.fields['reqtype'] = 'fileupload'; request.files.add(file); - final response = await client.sendRequest(request); + final response = await http.Response.fromStream( + await appHttpClient.send(request), + ); + ServerClient.checkResponseSuccess(request.url, response); + return response.body; case ImageStore.imgLink: const path = 'https://imglink.io/upload'; @@ -104,7 +107,10 @@ class APIImages { request.files.add(file); - final response = await client.sendRequest(request); + final response = await http.Response.fromStream( + await appHttpClient.send(request), + ); + ServerClient.checkResponseSuccess(request.url, response); return ((response.bodyJson['images']! as List).first as JsonMap)['direct_link']! diff --git a/lib/src/api/microblogs.dart b/lib/src/api/microblogs.dart index 9a12b8f0..e981750d 100644 --- a/lib/src/api/microblogs.dart +++ b/lib/src/api/microblogs.dart @@ -116,23 +116,22 @@ class MbinAPIMicroblogs { }) async { final path = '/magazine/$communityId/posts/image'; - final request = http.MultipartRequest( - 'POST', - Uri.https(client.domain, client.software.apiPathPrefix + path), - ); - - final multipartFile = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: image.name, - contentType: MediaType.parse(image.mimeType!), + final response = await client.postMultipart( + path, + builder: (request) async { + final multipartFile = http.MultipartFile.fromBytes( + 'uploadImage', + await image.readAsBytes(), + filename: image.name, + contentType: MediaType.parse(image.mimeType!), + ); + request.files.add(multipartFile); + request.fields['body'] = body; + request.fields['lang'] = lang; + request.fields['isAdult'] = isAdult.toString(); + request.fields['alt'] = alt; + }, ); - request.files.add(multipartFile); - request.fields['body'] = body; - request.fields['lang'] = lang; - request.fields['isAdult'] = isAdult.toString(); - request.fields['alt'] = alt; - final response = await client.sendRequest(request); return PostModel.fromMbinPost(response.bodyJson); } diff --git a/lib/src/api/threads.dart b/lib/src/api/threads.dart index 0082e911..13e1f9d3 100644 --- a/lib/src/api/threads.dart +++ b/lib/src/api/threads.dart @@ -394,61 +394,59 @@ class APIThreads { final path = '/magazine/$communityId/entries'; - final request = http.MultipartRequest( - 'POST', - Uri.https(client.domain, client.software.apiPathPrefix + path), + final response = await client.postMultipart( + path, + builder: (request) async { + request.fields['title'] = title; + if (url != null) { + request.fields['url'] = url; + } + for (var i = 0; i < tags.length; ++i) { + request.fields['tags[$i]'] = tags[i]; + } + request.fields['isOc'] = isOc.toString(); + if (body != null && body.isNotEmpty) { + request.fields['body'] = body; + } + request.fields['lang'] = lang; + request.fields['isAdult'] = isAdult.toString(); + if (alt != null) { + request.fields['alt'] = alt; + } + if (image != null) { + final file = http.MultipartFile.fromBytes( + 'uploadImage', + await image.readAsBytes(), + filename: image.name, + contentType: MediaType.parse( + image.mimeType ?? lookupMimeType(image.path)!, + ), + ); + request.files.add(file); + } + }, ); - request.fields['title'] = title; - if (url != null) { - request.fields['url'] = url; - } - for (var i = 0; i < tags.length; ++i) { - request.fields['tags[$i]'] = tags[i]; - } - request.fields['isOc'] = isOc.toString(); - if (body != null && body.isNotEmpty) { - request.fields['body'] = body; - } - request.fields['lang'] = lang; - request.fields['isAdult'] = isAdult.toString(); - if (alt != null) { - request.fields['alt'] = alt; - } - if (image != null) { - final file = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: image.name, - contentType: MediaType.parse( - image.mimeType ?? lookupMimeType(image.path)!, - ), - ); - request.files.add(file); - } - - final response = await client.sendRequest(request); - return PostModel.fromMbinEntry(response.bodyJson); case ServerSoftware.lemmy: if (image != null) { const uploadPath = '/pictrs/image'; - final uploadRequest = http.MultipartRequest( - 'POST', - Uri.https(client.domain, uploadPath), - ); - final multipartFile = http.MultipartFile.fromBytes( - 'images[]', - await image.readAsBytes(), - filename: image.name, - contentType: MediaType.parse( - image.mimeType ?? lookupMimeType(image.path)!, - ), + final pictrsResponse = await client.postMultipart( + uploadPath, + builder: (request) async { + final multipartFile = http.MultipartFile.fromBytes( + 'images[]', + await image.readAsBytes(), + filename: image.name, + contentType: MediaType.parse( + image.mimeType ?? lookupMimeType(image.path)!, + ), + ); + request.files.add(multipartFile); + }, ); - uploadRequest.files.add(multipartFile); - final pictrsResponse = await client.sendRequest(uploadRequest); final imageName = ((pictrsResponse.bodyJson['files']! as List).first! @@ -481,24 +479,20 @@ class APIThreads { if (image != null) { const uploadPath = '/upload/image'; - final uploadRequest = http.MultipartRequest( - 'POST', - Uri.https( - client.domain, - client.software.apiPathPrefix + uploadPath, - ), - ); - final multipartFile = http.MultipartFile.fromBytes( - 'file', - await image.readAsBytes(), - filename: image.name, - contentType: MediaType.parse( - image.mimeType ?? lookupMimeType(image.path)!, - ), + final uploadResponse = await client.postMultipart( + uploadPath, + builder: (request) async { + final multipartFile = http.MultipartFile.fromBytes( + 'file', + await image.readAsBytes(), + filename: image.name, + contentType: MediaType.parse( + image.mimeType ?? lookupMimeType(image.path)!, + ), + ); + request.files.add(multipartFile); + }, ); - uploadRequest.files.add(multipartFile); - - final uploadResponse = await client.sendRequest(uploadRequest); final imageUrl = uploadResponse.bodyJson['url'] as String?; diff --git a/lib/src/api/users.dart b/lib/src/api/users.dart index e2a2f5db..111341e5 100644 --- a/lib/src/api/users.dart +++ b/lib/src/api/users.dart @@ -270,36 +270,36 @@ class APIUsers { case ServerSoftware.mbin: const path = '/users/avatar'; - final request = http.MultipartRequest( - 'POST', - Uri.https(client.domain, client.software.apiPathPrefix + path), - ); - final multipartFile = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), + final response = await client.postMultipart( + path, + builder: (request) async { + final multipartFile = http.MultipartFile.fromBytes( + 'uploadImage', + await image.readAsBytes(), + filename: basename(image.path), + contentType: MediaType.parse(lookupMimeType(image.path)!), + ); + request.files.add(multipartFile); + }, ); - request.files.add(multipartFile); - final response = await client.sendRequest(request); return DetailedUserModel.fromMbin(response.bodyJson); case ServerSoftware.lemmy: const pictrsPath = '/pictrs/image'; - final uploadRequest = http.MultipartRequest( - 'POST', - Uri.https(client.domain, pictrsPath), - ); - final multipartFile = http.MultipartFile.fromBytes( - 'images[]', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), + final pictrsResponse = await client.postMultipart( + pictrsPath, + builder: (request) async { + final multipartFile = http.MultipartFile.fromBytes( + 'images[]', + await image.readAsBytes(), + filename: basename(image.path), + contentType: MediaType.parse(lookupMimeType(image.path)!), + ); + request.files.add(multipartFile); + }, ); - uploadRequest.files.add(multipartFile); - final pictrsResponse = await client.sendRequest(uploadRequest); final imageName = ((pictrsResponse.bodyJson['files']! as List).first! @@ -318,18 +318,18 @@ class APIUsers { case ServerSoftware.piefed: const uploadPath = '/upload/user_image'; - final uploadRequest = http.MultipartRequest( - 'POST', - Uri.https(client.domain, client.software.apiPathPrefix + uploadPath), - ); - final multipartFile = http.MultipartFile.fromBytes( - 'file', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), + final uploadResponse = await client.postMultipart( + uploadPath, + builder: (request) async { + final multipartFile = http.MultipartFile.fromBytes( + 'file', + await image.readAsBytes(), + filename: basename(image.path), + contentType: MediaType.parse(lookupMimeType(image.path)!), + ); + request.files.add(multipartFile); + }, ); - uploadRequest.files.add(multipartFile); - final uploadResponse = await client.sendRequest(uploadRequest); final imageUrl = uploadResponse.bodyJson['url'] as String?; @@ -370,36 +370,36 @@ class APIUsers { case ServerSoftware.mbin: const path = '/users/cover'; - final request = http.MultipartRequest( - 'POST', - Uri.https(client.domain, client.software.apiPathPrefix + path), - ); - final multipartFile = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), + final response = await client.postMultipart( + path, + builder: (request) async { + final multipartFile = http.MultipartFile.fromBytes( + 'uploadImage', + await image.readAsBytes(), + filename: basename(image.path), + contentType: MediaType.parse(lookupMimeType(image.path)!), + ); + request.files.add(multipartFile); + }, ); - request.files.add(multipartFile); - final response = await client.sendRequest(request); return DetailedUserModel.fromMbin(response.bodyJson); case ServerSoftware.lemmy: const pictrsPath = '/pictrs/image'; - final request = http.MultipartRequest( - 'POST', - Uri.https(client.domain, pictrsPath), - ); - final multipartFile = http.MultipartFile.fromBytes( - 'images[]', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), + final pictrsResponse = await client.postMultipart( + pictrsPath, + builder: (request) async { + final multipartFile = http.MultipartFile.fromBytes( + 'images[]', + await image.readAsBytes(), + filename: basename(image.path), + contentType: MediaType.parse(lookupMimeType(image.path)!), + ); + request.files.add(multipartFile); + }, ); - request.files.add(multipartFile); - final pictrsResponse = await client.sendRequest(request); final imageName = ((pictrsResponse.bodyJson['files']! as List).first! @@ -418,18 +418,18 @@ class APIUsers { case ServerSoftware.piefed: const uploadPath = '/upload/user_image'; - final uploadRequest = http.MultipartRequest( - 'POST', - Uri.https(client.domain, client.software.apiPathPrefix + uploadPath), - ); - final multipartFile = http.MultipartFile.fromBytes( - 'file', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), + final uploadResponse = await client.postMultipart( + uploadPath, + builder: (request) async { + final multipartFile = http.MultipartFile.fromBytes( + 'file', + await image.readAsBytes(), + filename: basename(image.path), + contentType: MediaType.parse(lookupMimeType(image.path)!), + ); + request.files.add(multipartFile); + }, ); - uploadRequest.files.add(multipartFile); - final uploadResponse = await client.sendRequest(uploadRequest); final imageUrl = uploadResponse.bodyJson['url'] as String?; From 07514782b45674e229c64036b4188d1364411420 Mon Sep 17 00:00:00 2001 From: jwr1 <47087725+jwr1@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:27:03 -0500 Subject: [PATCH 2/2] Add arguments for fields and files instead of using request builder --- lib/src/api/client.dart | 37 +++++++++++++++++++--- lib/src/api/comments.dart | 18 ++++------- lib/src/api/images.dart | 20 ++---------- lib/src/api/microblogs.dart | 18 ++++------- lib/src/api/threads.dart | 62 +++++++------------------------------ lib/src/api/users.dart | 60 ++++------------------------------- 6 files changed, 64 insertions(+), 151 deletions(-) diff --git a/lib/src/api/client.dart b/lib/src/api/client.dart index b0a8a1a3..8e927857 100644 --- a/lib/src/api/client.dart +++ b/lib/src/api/client.dart @@ -2,8 +2,12 @@ import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; +import 'package:http_parser/http_parser.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:interstellar/src/controller/server.dart'; import 'package:interstellar/src/utils/utils.dart'; +import 'package:mime/mime.dart'; +import 'package:path/path.dart'; class RestrictedAuthException implements Exception { RestrictedAuthException(this.message, this.uri); @@ -77,22 +81,45 @@ class ServerClient { Future postMultipart( String path, { Map? queryParams, - FutureOr Function(http.MultipartRequest request)? builder, - }) => - _sendMultipart('POST', path, queryParams: queryParams, builder: builder); + Map? fields, + Map? files, + }) => _sendMultipart( + 'POST', + path, + queryParams: queryParams, + fields: fields, + files: files, + ); Future _sendMultipart( String method, String path, { Map? queryParams, - FutureOr Function(http.MultipartRequest request)? builder, + Map? fields, + Map? files, }) async { final request = http.MultipartRequest( method, _uri(path, queryParams: queryParams), ); - await builder?.call(request); + if (fields != null) request.fields.addAll(fields); + for (final entry in (files ?? {}).entries) { + final name = entry.key; + final file = entry.value; + + final filename = basename(file.path); + final mime = lookupMimeType(filename); + + request.files.add( + http.MultipartFile.fromBytes( + name, + await file.readAsBytes(), + filename: filename, + contentType: mime == null ? null : MediaType.parse(mime), + ), + ); + } return _sendRequest(request); } diff --git a/lib/src/api/comments.dart b/lib/src/api/comments.dart index e314e99e..c65696b2 100644 --- a/lib/src/api/comments.dart +++ b/lib/src/api/comments.dart @@ -301,19 +301,13 @@ class APIComments { final response = await client.postMultipart( path, - builder: (request) async { - final file = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(file); - request.fields['body'] = body; - request.fields['lang'] = lang; - request.fields['isAdult'] = isAdult.toString(); - request.fields['alt'] = alt ?? ''; + fields: { + 'body': body, + 'lang': lang, + 'isAdult': isAdult.toString(), + 'alt': alt ?? '', }, + files: {'uploadImage': image}, ); return CommentModel.fromMbin(response.bodyJson); diff --git a/lib/src/api/images.dart b/lib/src/api/images.dart index cd40d6f5..b8bfcac7 100644 --- a/lib/src/api/images.dart +++ b/lib/src/api/images.dart @@ -38,15 +38,7 @@ class APIImages { final response = await client.postMultipart( path, - builder: (request) async { - final file = http.MultipartFile.fromBytes( - 'images[]', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(file); - }, + files: {'images[]': image}, ); final imageName = @@ -61,15 +53,7 @@ class APIImages { final response = await client.postMultipart( path, - builder: (request) async { - final file = http.MultipartFile.fromBytes( - 'file', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(file); - }, + files: {'file': image}, ); return response.bodyJson['url']! as String; diff --git a/lib/src/api/microblogs.dart b/lib/src/api/microblogs.dart index e981750d..4442a811 100644 --- a/lib/src/api/microblogs.dart +++ b/lib/src/api/microblogs.dart @@ -118,19 +118,13 @@ class MbinAPIMicroblogs { final response = await client.postMultipart( path, - builder: (request) async { - final multipartFile = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: image.name, - contentType: MediaType.parse(image.mimeType!), - ); - request.files.add(multipartFile); - request.fields['body'] = body; - request.fields['lang'] = lang; - request.fields['isAdult'] = isAdult.toString(); - request.fields['alt'] = alt; + fields: { + 'body': body, + 'lang': lang, + 'isAdult': isAdult.toString(), + 'alt': alt, }, + files: {'uploadImage': image}, ); return PostModel.fromMbinPost(response.bodyJson); diff --git a/lib/src/api/threads.dart b/lib/src/api/threads.dart index 13e1f9d3..14f78547 100644 --- a/lib/src/api/threads.dart +++ b/lib/src/api/threads.dart @@ -396,35 +396,17 @@ class APIThreads { final response = await client.postMultipart( path, - builder: (request) async { - request.fields['title'] = title; - if (url != null) { - request.fields['url'] = url; - } - for (var i = 0; i < tags.length; ++i) { - request.fields['tags[$i]'] = tags[i]; - } - request.fields['isOc'] = isOc.toString(); - if (body != null && body.isNotEmpty) { - request.fields['body'] = body; - } - request.fields['lang'] = lang; - request.fields['isAdult'] = isAdult.toString(); - if (alt != null) { - request.fields['alt'] = alt; - } - if (image != null) { - final file = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: image.name, - contentType: MediaType.parse( - image.mimeType ?? lookupMimeType(image.path)!, - ), - ); - request.files.add(file); - } + fields: { + 'title': title, + 'url': ?url, + for (var i = 0; i < tags.length; ++i) 'tags[$i]': tags[i], + 'isOc': isOc.toString(), + if (body != null && body.isNotEmpty) 'body': body, + 'lang': lang, + 'isAdult': isAdult.toString(), + 'alt': ?alt, }, + files: {'uploadImage': ?image}, ); return PostModel.fromMbinEntry(response.bodyJson); @@ -435,17 +417,7 @@ class APIThreads { final pictrsResponse = await client.postMultipart( uploadPath, - builder: (request) async { - final multipartFile = http.MultipartFile.fromBytes( - 'images[]', - await image.readAsBytes(), - filename: image.name, - contentType: MediaType.parse( - image.mimeType ?? lookupMimeType(image.path)!, - ), - ); - request.files.add(multipartFile); - }, + files: {'images[]': image}, ); final imageName = @@ -481,17 +453,7 @@ class APIThreads { final uploadResponse = await client.postMultipart( uploadPath, - builder: (request) async { - final multipartFile = http.MultipartFile.fromBytes( - 'file', - await image.readAsBytes(), - filename: image.name, - contentType: MediaType.parse( - image.mimeType ?? lookupMimeType(image.path)!, - ), - ); - request.files.add(multipartFile); - }, + files: {'file': image}, ); final imageUrl = uploadResponse.bodyJson['url'] as String?; diff --git a/lib/src/api/users.dart b/lib/src/api/users.dart index 111341e5..edaea714 100644 --- a/lib/src/api/users.dart +++ b/lib/src/api/users.dart @@ -272,15 +272,7 @@ class APIUsers { final response = await client.postMultipart( path, - builder: (request) async { - final multipartFile = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(multipartFile); - }, + files: {'uploadImage': image}, ); return DetailedUserModel.fromMbin(response.bodyJson); @@ -290,15 +282,7 @@ class APIUsers { final pictrsResponse = await client.postMultipart( pictrsPath, - builder: (request) async { - final multipartFile = http.MultipartFile.fromBytes( - 'images[]', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(multipartFile); - }, + files: {'images[]': image}, ); final imageName = @@ -320,15 +304,7 @@ class APIUsers { final uploadResponse = await client.postMultipart( uploadPath, - builder: (request) async { - final multipartFile = http.MultipartFile.fromBytes( - 'file', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(multipartFile); - }, + files: {'file': image}, ); final imageUrl = uploadResponse.bodyJson['url'] as String?; @@ -372,15 +348,7 @@ class APIUsers { final response = await client.postMultipart( path, - builder: (request) async { - final multipartFile = http.MultipartFile.fromBytes( - 'uploadImage', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(multipartFile); - }, + files: {'uploadImage': image}, ); return DetailedUserModel.fromMbin(response.bodyJson); @@ -390,15 +358,7 @@ class APIUsers { final pictrsResponse = await client.postMultipart( pictrsPath, - builder: (request) async { - final multipartFile = http.MultipartFile.fromBytes( - 'images[]', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(multipartFile); - }, + files: {'images[]': image}, ); final imageName = @@ -420,15 +380,7 @@ class APIUsers { final uploadResponse = await client.postMultipart( uploadPath, - builder: (request) async { - final multipartFile = http.MultipartFile.fromBytes( - 'file', - await image.readAsBytes(), - filename: basename(image.path), - contentType: MediaType.parse(lookupMimeType(image.path)!), - ); - request.files.add(multipartFile); - }, + files: {'file': image}, ); final imageUrl = uploadResponse.bodyJson['url'] as String?;