From 45156d57fb0642e9c6e46b1dc0c8ee70660ee38e Mon Sep 17 00:00:00 2001 From: shilangyu Date: Sat, 24 Oct 2020 00:11:44 +0200 Subject: [PATCH 1/5] update LAC, add image_picker --- ios/Runner/Info.plist | 8 ++++++++ pubspec.lock | 23 ++++++++++++++++++++++- pubspec.yaml | 3 ++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index d87d758..18954dc 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -41,5 +41,13 @@ UIViewControllerBasedStatusBarAppearance + + + NSPhotoLibraryUsageDescription + For uploading images for posts/avatars + NSCameraUsageDescription + For uploading images for posts/avatars + NSMicrophoneUsageDescription + For recording videos for posts diff --git a/pubspec.lock b/pubspec.lock index 621e2d1..3ddc400 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -265,6 +265,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0+2" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.11" flutter_slidable: dependency: "direct main" description: @@ -338,6 +345,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.4" + image_picker: + dependency: "direct main" + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.7+12" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" intl: dependency: transitive description: @@ -379,7 +400,7 @@ packages: name: lemmy_api_client url: "https://pub.dartlang.org" source: hosted - version: "0.6.0" + version: "0.7.2" logging: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2900aac..bf54703 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: url_launcher: ^5.5.1 shared_preferences: ">=0.5.0 <2.0.0" package_info: ^0.4.3 + image_picker: ^0.6.7 # state management flutter_hooks: ^0.13.2 @@ -44,7 +45,7 @@ dependencies: # utils timeago: ^2.0.27 fuzzy: <1.0.0 - lemmy_api_client: ^0.6.0 + lemmy_api_client: ^0.7.2 flutter: sdk: flutter From 454468c0e7354b2a251bf33de71ac7de0ded14bc Mon Sep 17 00:00:00 2001 From: shilangyu Date: Sat, 24 Oct 2020 00:12:42 +0200 Subject: [PATCH 2/5] add image upload to post creation --- lib/hooks/image_picker.dart | 4 ++++ lib/pages/create_post.dart | 45 +++++++++++++++++++++++++++++++------ lib/util/pictrs.dart | 2 ++ 3 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 lib/hooks/image_picker.dart create mode 100644 lib/util/pictrs.dart diff --git a/lib/hooks/image_picker.dart b/lib/hooks/image_picker.dart new file mode 100644 index 0000000..8f2babd --- /dev/null +++ b/lib/hooks/image_picker.dart @@ -0,0 +1,4 @@ +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:image_picker/image_picker.dart'; + +ImagePicker useImagePicker() => useMemoized(() => ImagePicker()); diff --git a/lib/pages/create_post.dart b/lib/pages/create_post.dart index 7535aa8..0fc13c3 100644 --- a/lib/pages/create_post.dart +++ b/lib/pages/create_post.dart @@ -1,14 +1,17 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; import '../hooks/delayed_loading.dart'; +import '../hooks/image_picker.dart'; import '../hooks/logged_in_action.dart'; import '../hooks/memo_future.dart'; import '../hooks/stores.dart'; import '../util/extensions/api.dart'; import '../util/goto.dart'; +import '../util/pictrs.dart'; import '../util/spaced.dart'; import '../widgets/markdown_text.dart'; import 'full_post.dart'; @@ -48,6 +51,8 @@ class CreatePost extends HookWidget { final showFancy = useState(false); final nsfw = useState(false); final delayed = useDelayedLoading(); + final imagePicker = useImagePicker(); + final imageUploadLoading = useState(false); final allCommunitiesSnap = useMemoFuture( () => LemmyApi(selectedInstance.value) @@ -66,6 +71,21 @@ class CreatePost extends HookWidget { [selectedInstance.value], ); + uploadPicture() async { + final pic = await imagePicker.getImage(source: ImageSource.gallery); + // pic is null when the picker was cancelled + if (pic != null) { + imageUploadLoading.value = true; + final pictrs = LemmyApi(selectedInstance.value).pictrs; + // TODO: consider auto removing pictures when not used + // TODO: would help with preventing people from abusing uploads + final upload = await pictrs.upload(pic.path); + urlController.text = + pathToPictrs(selectedInstance.value, upload.files[0].file); + imageUploadLoading.value = false; + } + } + // TODO: use drop down from AddAccountPage final instanceDropdown = InputDecorator( decoration: const InputDecoration( @@ -113,13 +133,24 @@ class CreatePost extends HookWidget { ), ); - final url = TextField( - controller: urlController, - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'URL', - suffixIcon: Icon(Icons.link)), - ); + final url = Row(children: [ + Expanded( + child: TextField( + controller: urlController, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'URL', + suffixIcon: Icon(Icons.link)), + ), + ), + SizedBox(width: 5), + IconButton( + icon: imageUploadLoading.value + ? CircularProgressIndicator() + : Icon(Icons.add_photo_alternate), + onPressed: uploadPicture, + ) + ]); final title = TextField( controller: titleController, diff --git a/lib/util/pictrs.dart b/lib/util/pictrs.dart new file mode 100644 index 0000000..e0b7d55 --- /dev/null +++ b/lib/util/pictrs.dart @@ -0,0 +1,2 @@ +String pathToPictrs(String instanceUrl, String imgId) => + 'https://$instanceUrl/pictrs/image/$imgId'; From affd078fc8af846978c3b54bb58ee147096e776d Mon Sep 17 00:00:00 2001 From: shilangyu Date: Sat, 24 Oct 2020 11:26:08 +0200 Subject: [PATCH 3/5] bump LAC --- pubspec.lock | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 3ddc400..dd26301 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -400,7 +400,7 @@ packages: name: lemmy_api_client url: "https://pub.dartlang.org" source: hosted - version: "0.7.2" + version: "0.7.3" logging: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index bf54703..b087627 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,7 +45,7 @@ dependencies: # utils timeago: ^2.0.27 fuzzy: <1.0.0 - lemmy_api_client: ^0.7.2 + lemmy_api_client: ^0.7.3 flutter: sdk: flutter From a41e5c19c388acf1956f488f75ba0b0fda33ee65 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Sat, 24 Oct 2020 11:26:52 +0200 Subject: [PATCH 4/5] add option to remove pictures --- lib/pages/create_post.dart | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/pages/create_post.dart b/lib/pages/create_post.dart index 0fc13c3..c998c21 100644 --- a/lib/pages/create_post.dart +++ b/lib/pages/create_post.dart @@ -53,6 +53,7 @@ class CreatePost extends HookWidget { final delayed = useDelayedLoading(); final imagePicker = useImagePicker(); final imageUploadLoading = useState(false); + final pictrsDeleteToken = useState(null); final allCommunitiesSnap = useMemoFuture( () => LemmyApi(selectedInstance.value) @@ -76,16 +77,27 @@ class CreatePost extends HookWidget { // pic is null when the picker was cancelled if (pic != null) { imageUploadLoading.value = true; + final pictrs = LemmyApi(selectedInstance.value).pictrs; - // TODO: consider auto removing pictures when not used - // TODO: would help with preventing people from abusing uploads final upload = await pictrs.upload(pic.path); + + pictrsDeleteToken.value = upload.files[0]; urlController.text = pathToPictrs(selectedInstance.value, upload.files[0].file); imageUploadLoading.value = false; } } + removePicture() { + LemmyApi(selectedInstance.value) + .pictrs + .delete(pictrsDeleteToken.value) + .catchError((_) {}); + + pictrsDeleteToken.value = null; + urlController.text = ''; + } + // TODO: use drop down from AddAccountPage final instanceDropdown = InputDecorator( decoration: const InputDecoration( @@ -136,6 +148,7 @@ class CreatePost extends HookWidget { final url = Row(children: [ Expanded( child: TextField( + enabled: pictrsDeleteToken.value == null, controller: urlController, decoration: InputDecoration( border: OutlineInputBorder(), @@ -147,8 +160,13 @@ class CreatePost extends HookWidget { IconButton( icon: imageUploadLoading.value ? CircularProgressIndicator() - : Icon(Icons.add_photo_alternate), - onPressed: uploadPicture, + : Icon(pictrsDeleteToken.value == null + ? Icons.add_photo_alternate + : Icons.close), + onPressed: + pictrsDeleteToken.value == null ? uploadPicture : removePicture, + tooltip: + pictrsDeleteToken.value == null ? 'Add picture' : 'Delete picture', ) ]); From 93c0e883d3d9835f3991ccc93e1b78ccf430009f Mon Sep 17 00:00:00 2001 From: shilangyu Date: Sat, 24 Oct 2020 19:58:38 +0200 Subject: [PATCH 5/5] error handling on image upload --- lib/pages/create_post.dart | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/pages/create_post.dart b/lib/pages/create_post.dart index c998c21..c2c6d4b 100644 --- a/lib/pages/create_post.dart +++ b/lib/pages/create_post.dart @@ -73,17 +73,24 @@ class CreatePost extends HookWidget { ); uploadPicture() async { - final pic = await imagePicker.getImage(source: ImageSource.gallery); - // pic is null when the picker was cancelled - if (pic != null) { - imageUploadLoading.value = true; + try { + final pic = await imagePicker.getImage(source: ImageSource.gallery); + // pic is null when the picker was cancelled + if (pic != null) { + imageUploadLoading.value = true; - final pictrs = LemmyApi(selectedInstance.value).pictrs; - final upload = await pictrs.upload(pic.path); + final pictrs = LemmyApi(selectedInstance.value).pictrs; + final upload = await pictrs.upload(pic.path); - pictrsDeleteToken.value = upload.files[0]; - urlController.text = - pathToPictrs(selectedInstance.value, upload.files[0].file); + pictrsDeleteToken.value = upload.files[0]; + urlController.text = + pathToPictrs(selectedInstance.value, upload.files[0].file); + } + // ignore: avoid_catches_without_on_clauses + } catch (e) { + scaffoldKey.currentState + .showSnackBar(SnackBar(content: Text('Failed to upload image'))); + } finally { imageUploadLoading.value = false; } }