media attachments
This commit is contained in:
parent
4ac218b065
commit
8aa49dc2ea
|
@ -29,11 +29,13 @@ class InstanceConfiguration {
|
|||
|
||||
class StatusConfiguration {
|
||||
final int maxChars;
|
||||
final int maxMediaAttachments;
|
||||
|
||||
StatusConfiguration(this.maxChars);
|
||||
StatusConfiguration(this.maxChars, this.maxMediaAttachments);
|
||||
static StatusConfiguration fromJson(Map<String, dynamic> json) {
|
||||
return StatusConfiguration(
|
||||
json["max_characters"],
|
||||
json["max_media_attachments"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:localization/localization.dart';
|
||||
import 'package:loris/business_logic/fileupload/fileupload.dart';
|
||||
import 'package:loris/business_logic/instance/instance.dart';
|
||||
import 'package:loris/business_logic/network_tools/get_post_from_url.dart';
|
||||
import 'package:loris/business_logic/timeline/timeline.dart' as tl;
|
||||
import '../business_logic/posting/posting.dart';
|
||||
import '../partials/post.dart';
|
||||
import 'package:loris/global.dart' as global;
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
|
||||
class MakePost extends StatefulWidget {
|
||||
const MakePost({Key? key, this.inReplyTo}) : super(key: key);
|
||||
|
@ -18,26 +21,57 @@ class MakePost extends StatefulWidget {
|
|||
class _MakePostState extends State<MakePost> {
|
||||
String replyAts = "";
|
||||
String accountid = global.settings!.activeIdentity;
|
||||
int? maxLength;
|
||||
InstanceInformation? instanceInfo;
|
||||
String text = "";
|
||||
String spoilerText = "";
|
||||
// tracks fileuploads and their ids on servers
|
||||
// if the id is null the file has not been uploaded yet
|
||||
List<FileUpload> files = [];
|
||||
// stores all identities and if available the post id you are replying to
|
||||
Map<String, String?> identitiesAvailable = {};
|
||||
tl.Visibility visibility = tl.Visibility.public;
|
||||
|
||||
void switchAccount(String acct) {
|
||||
setState(() {
|
||||
maxLength = null;
|
||||
instanceInfo = null;
|
||||
accountid = acct;
|
||||
});
|
||||
updateMaxChars();
|
||||
}
|
||||
|
||||
bool tooManyAttachments() {
|
||||
if (instanceInfo == null) {
|
||||
return true;
|
||||
}
|
||||
if (instanceInfo!.configuration.statusconfig.maxMediaAttachments <
|
||||
files.length) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// moves file in list up or down,
|
||||
// has to be called with file from items list
|
||||
void moveFile(FileUpload file, {up = true}) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
final index = files.indexOf(file);
|
||||
final newIndex = (index + (up ? -1 : 1)) % files.length;
|
||||
print(newIndex);
|
||||
|
||||
files.remove(file);
|
||||
setState(() {
|
||||
files.insert(newIndex, file);
|
||||
});
|
||||
}
|
||||
|
||||
void updateMaxChars() async {
|
||||
final info = await instanceInformationForIdentity(accountid);
|
||||
if (info.keys.first == 200) {
|
||||
if (info.keys.first == 200 && mounted) {
|
||||
setState(() {
|
||||
maxLength = info.values.first!.configuration.statusconfig.maxChars;
|
||||
instanceInfo = info.values.first!;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +134,7 @@ class _MakePostState extends State<MakePost> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// make list of all different widgets to be displayed
|
||||
List<Widget> c = [];
|
||||
if (widget.inReplyTo != null) {
|
||||
c.add(
|
||||
|
@ -140,9 +175,6 @@ class _MakePostState extends State<MakePost> {
|
|||
)));
|
||||
}
|
||||
List<Widget> actionButtons = [
|
||||
maxLength == null
|
||||
? const CircularProgressIndicator()
|
||||
: SelectableText((maxLength! - text.length).toString()),
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton<tl.Visibility>(
|
||||
alignment: Alignment.center,
|
||||
|
@ -156,6 +188,31 @@ class _MakePostState extends State<MakePost> {
|
|||
},
|
||||
),
|
||||
),
|
||||
// media attachment button
|
||||
TextButton.icon(
|
||||
onPressed: () async {
|
||||
// open filepicker
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
withData: false,
|
||||
allowMultiple: true,
|
||||
);
|
||||
if (result != null) {
|
||||
// if there are any files
|
||||
// then parse them
|
||||
for (var path in result.paths) {
|
||||
if (path != null && mounted) {
|
||||
print(path);
|
||||
final FileUpload f = await FileUpload.fromPath(path, "");
|
||||
setState(() {
|
||||
files.add(f);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.attachment),
|
||||
label: Text(
|
||||
"${"add-file".i18n()}${instanceInfo == null ? "(${"loading...".i18n()})" : " (${instanceInfo!.configuration.statusconfig.maxMediaAttachments - files.length} ${"remaining".i18n()})"}")),
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton<String>(
|
||||
alignment: Alignment.center,
|
||||
|
@ -173,19 +230,24 @@ class _MakePostState extends State<MakePost> {
|
|||
"send-post".i18n(),
|
||||
),
|
||||
// send the post!!!
|
||||
onPressed: () async {
|
||||
final model = MakePostModel(
|
||||
spoilerText: spoilerText.trim(),
|
||||
identity: accountid,
|
||||
status: text,
|
||||
visibility: visibility,
|
||||
inReplyToId: widget.inReplyTo == null
|
||||
? null
|
||||
: identitiesAvailable[accountid]!,
|
||||
);
|
||||
model.sendPost();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
onPressed: ((spoilerText.isEmpty && text.isEmpty && files.isEmpty) ||
|
||||
tooManyAttachments())
|
||||
? null
|
||||
: () async {
|
||||
final model = MakePostModel(
|
||||
spoilerText: spoilerText.trim(),
|
||||
identity: accountid,
|
||||
status: text,
|
||||
visibility: visibility,
|
||||
inReplyToId: widget.inReplyTo == null
|
||||
? null
|
||||
: identitiesAvailable[accountid]!,
|
||||
);
|
||||
final result = await model.sendPost();
|
||||
if (result == 200) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.send),
|
||||
),
|
||||
];
|
||||
|
@ -194,6 +256,12 @@ class _MakePostState extends State<MakePost> {
|
|||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
initialValue: spoilerText,
|
||||
decoration: InputDecoration(
|
||||
counterText: instanceInfo == null
|
||||
? "loading...".i18n()
|
||||
: (instanceInfo!.configuration.statusconfig.maxChars -
|
||||
text.length -
|
||||
spoilerText.length)
|
||||
.toString(),
|
||||
prefixIcon: Icon(
|
||||
Icons.warning,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
|
@ -221,6 +289,10 @@ class _MakePostState extends State<MakePost> {
|
|||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
]);
|
||||
|
||||
// these are the action buttons
|
||||
c.add(
|
||||
Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 24,
|
||||
|
@ -229,7 +301,52 @@ class _MakePostState extends State<MakePost> {
|
|||
alignment: WrapAlignment.spaceAround,
|
||||
children: actionButtons,
|
||||
),
|
||||
]);
|
||||
);
|
||||
|
||||
// display media attachments
|
||||
for (var file in files) {
|
||||
c.add(Column(
|
||||
children: [
|
||||
FileUploadDisplay(file: file),
|
||||
TextField(
|
||||
onChanged: (value) => file.description = value,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
minLines: 1,
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
hintText: "file-description".i18n(),
|
||||
),
|
||||
),
|
||||
Wrap(
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
moveFile(file, up: false);
|
||||
},
|
||||
icon: const Icon(Icons.move_down),
|
||||
label: Text("move-down".i18n()),
|
||||
),
|
||||
TextButton.icon(
|
||||
onPressed: () => setState(() {
|
||||
files.remove(file);
|
||||
}),
|
||||
icon: const Icon(Icons.delete),
|
||||
label: Text(
|
||||
"remove-attached-file".i18n(),
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
moveFile(file);
|
||||
},
|
||||
icon: const Icon(Icons.move_up),
|
||||
label: Text("move-up".i18n()),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
return SimpleDialog(
|
||||
alignment: Alignment.center,
|
||||
|
@ -244,10 +361,7 @@ class _MakePostState extends State<MakePost> {
|
|||
width: (MediaQuery.of(context).size.width *
|
||||
global.settings!.postWidth) -
|
||||
56,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: global.settings!.maxPostWidth,
|
||||
minWidth: 375,
|
||||
),
|
||||
constraints: global.getConstraints(context),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
|
@ -259,3 +373,36 @@ class _MakePostState extends State<MakePost> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FileUploadDisplay extends StatelessWidget {
|
||||
const FileUploadDisplay({super.key, required this.file});
|
||||
final FileUpload file;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Divider(
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.attachment, size: 32),
|
||||
Expanded(
|
||||
child: SelectableText(
|
||||
"${file.path} (${(file.data.length / 1024).toStringAsFixed(2)}kb)",
|
||||
style: Theme.of(context).textTheme.displaySmall),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (lookupMimeType(file.path)!.startsWith("image/"))
|
||||
Image.asset(
|
||||
file.path,
|
||||
fit: BoxFit.fitWidth,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
35
pubspec.lock
35
pubspec.lock
|
@ -99,6 +99,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
file_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -122,7 +129,14 @@ packages:
|
|||
name: flutter_markdown
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.10+5"
|
||||
version: "0.6.10+6"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.7"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -217,6 +231,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0"
|
||||
mime:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mime
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -244,7 +265,7 @@ packages:
|
|||
name: path_provider_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.3"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -258,7 +279,7 @@ packages:
|
|||
name: plugin_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.3"
|
||||
process:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -279,7 +300,7 @@ packages:
|
|||
name: shared_preferences_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.12"
|
||||
version: "2.0.13"
|
||||
shared_preferences_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -328,7 +349,7 @@ packages:
|
|||
name: shelf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
version: "1.4.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -410,7 +431,7 @@ packages:
|
|||
name: url_launcher_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.17"
|
||||
version: "6.0.19"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -480,7 +501,7 @@ packages:
|
|||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
version: "3.0.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -51,6 +51,8 @@ dependencies:
|
|||
url_strategy: ^0.2.0
|
||||
universal_html: ^2.0.8
|
||||
universal_io: ^2.0.4
|
||||
file_picker: ^5.2.0
|
||||
mime: ^1.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue