basic attachments are live

This commit is contained in:
zoe 2022-09-25 20:07:47 +02:00
parent 8aa49dc2ea
commit d0235cac5d
3 changed files with 99 additions and 16 deletions

View File

@ -1,7 +1,10 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:loris/global.dart' as global;
import 'package:http/http.dart' as http;
class FileUpload {
final MultipartFile data;
String description;
final String path;
@ -10,18 +13,45 @@ class FileUpload {
Map<String, String> ids = {};
FileUpload({
required this.data,
required this.description,
required this.path,
this.ids = const {},
});
static Future<FileUpload> fromPath(String path, String description) async {
final data = await MultipartFile.fromPath("file", path);
return FileUpload(
data: data,
description: description,
path: path,
);
}
/*
sends a media attachments and returns the http status code
if the status code is 200, the String is the media attachments id,
otherwise it's the error response
*/
Future<MapEntry<int, String>> upload(String identityName) async {
final identity = global.settings!.identities[identityName]!;
final headers = {
...global.defaultHeaders,
...identity.getAuthHeaders(),
};
final uri =
Uri(scheme: "https", host: identity.instanceUrl, path: "/api/v2/media");
final request = MultipartRequest(
"POST",
uri,
);
request.headers.addAll(headers);
request.files.add(await MultipartFile.fromPath(
"file",
path,
));
request.fields.addAll({"description": description});
final response = await http.Response.fromStream(await request.send());
if (response.statusCode != 200 && response.statusCode != 202) {
return MapEntry(response.statusCode, jsonDecode(response.body)["error"]);
}
return MapEntry(response.statusCode, jsonDecode(response.body)["id"]);
}
}

View File

@ -11,17 +11,19 @@ class MakePostModel {
final Visibility visibility;
final String? scheduledAt;
final String? inReplyToId;
List<String> mediaIds;
MakePostModel({
required this.identity,
required this.status,
required this.spoilerText,
required this.visibility,
required this.mediaIds,
this.scheduledAt,
this.inReplyToId,
});
Future<int> sendPost() async {
Future<http.Response> sendPost() async {
final headers = global.settings!.identities[identity]!.getAuthHeaders();
headers.addAll(global.defaultHeaders);
@ -29,6 +31,7 @@ class MakePostModel {
"status": status,
"sensitive": false,
"visibility": visibility.queryParam,
"media_ids": mediaIds,
};
if (inReplyToId != null) {
@ -55,6 +58,6 @@ class MakePostModel {
headers: headers,
body: jsonEncode(params),
);
return response.statusCode;
return response;
}
}

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:localization/localization.dart';
import 'package:loris/business_logic/fileupload/fileupload.dart';
@ -9,6 +11,7 @@ import '../partials/post.dart';
import 'package:loris/global.dart' as global;
import 'package:file_picker/file_picker.dart';
import 'package:mime/mime.dart';
import 'package:universal_io/io.dart' as io;
class MakePost extends StatefulWidget {
const MakePost({Key? key, this.inReplyTo}) : super(key: key);
@ -24,6 +27,8 @@ class _MakePostState extends State<MakePost> {
InstanceInformation? instanceInfo;
String text = "";
String spoilerText = "";
String? status;
bool sending = false;
// tracks fileuploads and their ids on servers
// if the id is null the file has not been uploaded yet
List<FileUpload> files = [];
@ -201,7 +206,7 @@ class _MakePostState extends State<MakePost> {
// then parse them
for (var path in result.paths) {
if (path != null && mounted) {
print(path);
if (await io.Directory(path).exists()) break;
final FileUpload f = await FileUpload.fromPath(path, "");
setState(() {
files.add(f);
@ -212,7 +217,7 @@ class _MakePostState extends State<MakePost> {
},
icon: const Icon(Icons.attachment),
label: Text(
"${"add-file".i18n()}${instanceInfo == null ? "(${"loading...".i18n()})" : " (${instanceInfo!.configuration.statusconfig.maxMediaAttachments - files.length} ${"remaining".i18n()})"}")),
"${"add-files".i18n()}${instanceInfo == null ? "(${"loading...".i18n()})" : " (${instanceInfo!.configuration.statusconfig.maxMediaAttachments - files.length} ${"remaining".i18n()})"}")),
DropdownButtonHideUnderline(
child: DropdownButton<String>(
alignment: Alignment.center,
@ -234,7 +239,38 @@ class _MakePostState extends State<MakePost> {
tooManyAttachments())
? null
: () async {
setState(() {
sending = true;
status = "sending...".i18n();
});
// upload the media attachments
List<String> mediaIds = [];
for (var file in files) {
if (mounted) {
setState(() {
status =
"${"sending-file".i18n()}${mediaIds.length + 1}/${files.length}";
});
}
final response = await file.upload(accountid);
if (response.key == 200 || response.key == 202) {
mediaIds.add(response.value);
break;
}
if (mounted) {
setState(() {
status = response.value;
sending = false;
return;
});
}
}
print(mediaIds);
// send the final post
final model = MakePostModel(
mediaIds: mediaIds,
spoilerText: spoilerText.trim(),
identity: accountid,
status: text,
@ -244,8 +280,15 @@ class _MakePostState extends State<MakePost> {
: identitiesAvailable[accountid]!,
);
final result = await model.sendPost();
if (result == 200) {
Navigator.of(context).pop();
if (mounted) {
if (result.statusCode == 200) {
Navigator.of(context).pop();
}
setState(() {
sending = false;
status =
"${"failed-to-send".i18n()}: ${jsonDecode(result.body)["error"].toString()}";
});
}
},
icon: const Icon(Icons.send),
@ -351,10 +394,18 @@ class _MakePostState extends State<MakePost> {
return SimpleDialog(
alignment: Alignment.center,
contentPadding: const EdgeInsets.all(24),
title: SelectableText(
textAlign: TextAlign.center,
widget.inReplyTo == null ? "make-post".i18n() : "make-reply".i18n(),
style: Theme.of(context).textTheme.displayMedium),
title: Column(
children: [
SelectableText(
textAlign: TextAlign.center,
widget.inReplyTo == null
? "make-post".i18n()
: "make-reply".i18n(),
style: Theme.of(context).textTheme.displayMedium),
if (status != null) SelectableText(status!),
if (sending) const LinearProgressIndicator(),
],
),
children: [
SingleChildScrollView(
child: Container(
@ -391,8 +442,7 @@ class FileUploadDisplay extends StatelessWidget {
children: [
const Icon(Icons.attachment, size: 32),
Expanded(
child: SelectableText(
"${file.path} (${(file.data.length / 1024).toStringAsFixed(2)}kb)",
child: SelectableText(file.path,
style: Theme.of(context).textTheme.displaySmall),
),
],