diff --git a/lib/api/client.dart b/lib/api/client.dart index 740e486..dcd6c1d 100644 --- a/lib/api/client.dart +++ b/lib/api/client.dart @@ -54,6 +54,8 @@ class Client { 'User-Agent': 'Vikunja Mobile App' }; + get headers => _headers; + @override int get hashCode => _token.hashCode; diff --git a/lib/main.dart b/lib/main.dart index 1f80c0f..40e3be0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,6 +13,7 @@ import 'package:vikunja_app/pages/home.dart'; import 'package:vikunja_app/pages/user/login.dart'; import 'package:vikunja_app/theme/theme.dart'; import 'package:timezone/data/latest_all.dart' as tz; +import 'package:flutter_downloader/flutter_downloader.dart'; import 'managers/notifications.dart'; @@ -69,6 +70,7 @@ void main() async { Permission.notification.request(); } }); + await FlutterDownloader.initialize(); Workmanager().initialize(callbackDispatcher, isInDebugMode: false); runApp(VikunjaGlobal( child: new VikunjaApp( diff --git a/lib/models/taskAttachment.dart b/lib/models/taskAttachment.dart index b0c6cdc..9c3ab7c 100644 --- a/lib/models/taskAttachment.dart +++ b/lib/models/taskAttachment.dart @@ -2,11 +2,44 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:vikunja_app/models/user.dart'; +class TaskAttachmentFile { + final int id; + final DateTime created; + final String mime; + final String name; + final int size; + + TaskAttachmentFile({ + required this.id, + required this.created, + required this.mime, + required this.name, + required this.size, + }); + + TaskAttachmentFile.fromJSON(Map json) + : id = json['id'], + created = DateTime.parse(json['created']), + mime = json['mime'], + name = json['name'], + size = json['size']; + + toJSON() => { + 'id': id, + 'created': created.toUtc().toIso8601String(), + 'mime': mime, + 'name': name, + 'size': size, + }; +} + + @JsonSerializable() class TaskAttachment { final int id, taskId; final DateTime created; final User createdBy; + final TaskAttachmentFile file; // TODO: add file TaskAttachment({ @@ -14,12 +47,14 @@ class TaskAttachment { required this.taskId, DateTime? created, required this.createdBy, + required this.file, }) : this.created = created ?? DateTime.now(); TaskAttachment.fromJSON(Map json) : id = json['id'], taskId = json['task_id'], created = DateTime.parse(json['created']), + file = TaskAttachmentFile.fromJSON(json['file']), createdBy = User.fromJson(json['created_by']); toJSON() => { @@ -27,5 +62,6 @@ class TaskAttachment { 'task_id': taskId, 'created': created.toUtc().toIso8601String(), 'created_by': createdBy.toJSON(), + 'file': file.toJSON(), }; } \ No newline at end of file diff --git a/lib/pages/list/task_edit.dart b/lib/pages/list/task_edit.dart index dbf3fe5..f553025 100644 --- a/lib/pages/list/task_edit.dart +++ b/lib/pages/list/task_edit.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import 'package:vikunja_app/components/datetimePicker.dart'; @@ -113,7 +114,7 @@ class _TaskEditPageState extends State { key: _formKey, child: ListView( key: _listKey, - padding: const EdgeInsets.all(16.0), + padding: EdgeInsets.fromLTRB(16, 16, 16, MediaQuery.of(context).size.height / 2), children: [ Padding( padding: EdgeInsets.symmetric(vertical: 10.0), @@ -415,6 +416,33 @@ class _TaskEditPageState extends State { ], ), ), + ListView.separated( + separatorBuilder: (context, index) => Divider(), + padding: const EdgeInsets.all(16.0), + shrinkWrap: true, + itemCount: widget.task.attachments.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(widget.task.attachments[index].file.name), + trailing: IconButton( + icon: Icon(Icons.download), + onPressed: () { + String url = VikunjaGlobal.of(context).client.base; + url += '/tasks/${widget.task.id}/attachments/${widget.task.attachments[index].id}'; + print(url); + final taskId = FlutterDownloader.enqueue( + url: url, + fileName: widget.task.attachments[index].file.name, + headers: VikunjaGlobal.of(context).client.headers, // optional: header send with url (auth token etc) + savedDir: '/storage/emulated/0/Download/', + showNotification: true, // show download progress in status bar (for Android) + openFileFromNotification: true, // click on notification to open downloaded file (for Android) + ); + }, + ), + ); + }, + ) ], ), ), diff --git a/newdesign/Screenshot_20230514_013652.png b/newdesign/Screenshot_20230514_013652.png new file mode 100644 index 0000000..23295f4 Binary files /dev/null and b/newdesign/Screenshot_20230514_013652.png differ diff --git a/newdesign/Screenshot_20230514_013719.png b/newdesign/Screenshot_20230514_013719.png new file mode 100644 index 0000000..7e0b44f Binary files /dev/null and b/newdesign/Screenshot_20230514_013719.png differ diff --git a/newdesign/Screenshot_20230514_013732.png b/newdesign/Screenshot_20230514_013732.png new file mode 100644 index 0000000..4b15f7d Binary files /dev/null and b/newdesign/Screenshot_20230514_013732.png differ diff --git a/pubspec.lock b/pubspec.lock index 468d34b..3bd06a0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -278,6 +278,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.3" + flutter_downloader: + dependency: "direct main" + description: + name: flutter_downloader + sha256: e130001cf85d8d7450b8318a4670c19e495dd8c102ad4b15a512e9405e7c451d + url: "https://pub.dev" + source: hosted + version: "1.11.6" flutter_keyboard_visibility: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d6e8abc..c9334ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: dynamic_color: ^1.6.6 chewie: ^1.5.0 flutter_widget_from_html: ^0.14.10 + flutter_downloader: ^1.11.6 dev_dependencies: