diff --git a/lib/helpers/api_helper.dart b/lib/helpers/api_helper.dart index 41668be..f5d61f7 100644 --- a/lib/helpers/api_helper.dart +++ b/lib/helpers/api_helper.dart @@ -60,17 +60,6 @@ class APIHelper { contentType: v.type, ))); } - - // Process picked files - for (final key in request.pickedFiles.keys) { - var v = request.pickedFiles[key]; - data.files.add(MapEntry( - key, - MultipartFile.fromBytes( - await v.readAsBytes(), - filename: v.path.split("/").last, - ))); - } } // Execute the request diff --git a/lib/helpers/comments_helper.dart b/lib/helpers/comments_helper.dart index d42372a..2903165 100644 --- a/lib/helpers/comments_helper.dart +++ b/lib/helpers/comments_helper.dart @@ -17,7 +17,7 @@ class CommentsHelper { "content": comment.hasContent ? comment.content : "", }); - if (comment.hasImage) request.addPickedFile("image", comment.image); + if (comment.hasImage) request.addBytesFile("image", comment.image); final response = await request.execWithFiles(); diff --git a/lib/helpers/posts_helper.dart b/lib/helpers/posts_helper.dart index 7d598bf..f951963 100644 --- a/lib/helpers/posts_helper.dart +++ b/lib/helpers/posts_helper.dart @@ -142,7 +142,7 @@ class PostsHelper { break; case PostKind.IMAGE: - request.addPickedFile("image", post.image); + request.addBytesFile("image", post.image); break; case PostKind.WEB_LINK: diff --git a/lib/helpers/settings_helper.dart b/lib/helpers/settings_helper.dart index f6f429a..16dfa1f 100644 --- a/lib/helpers/settings_helper.dart +++ b/lib/helpers/settings_helper.dart @@ -5,7 +5,8 @@ import 'package:comunic/models/data_conservation_policy_settings.dart'; import 'package:comunic/models/general_settings.dart'; import 'package:comunic/models/new_emoji.dart'; import 'package:comunic/models/security_settings.dart'; -import 'package:image_picker/image_picker.dart'; + +import '../models/api_request.dart'; /// Settings helper /// @@ -92,11 +93,10 @@ class SettingsHelper { } /// Upload a new account image - static Future uploadAccountImage(PickedFile newImage) async => - (await APIRequest(uri: "settings/upload_account_image", needLogin: true) - .addPickedFile("picture", newImage) - .execWithFiles()) - .isOK; + static Future uploadAccountImage(BytesFile newImage) async => + await APIRequest(uri: "settings/upload_account_image", needLogin: true) + .addBytesFile("picture", newImage) + .execWithFilesAndThrow(); /// Upload a new account image from memory static Future uploadAccountImageFromMemory(List bytes) async => @@ -128,13 +128,12 @@ class SettingsHelper { /// Upload a new custom emoji static Future uploadNewCustomEmoji(NewEmoji newEmoji) async => - (await APIRequest( - uri: "settings/upload_custom_emoji", - needLogin: true, - args: {"shortcut": newEmoji.shortcut}) - .addPickedFile("image", newEmoji.image) - .execWithFiles()) - .assertOk(); + await APIRequest( + uri: "settings/upload_custom_emoji", + needLogin: true, + args: {"shortcut": newEmoji.shortcut}) + .addBytesFile("image", newEmoji.image) + .execWithFilesAndThrow(); /// Delete a custom emoji /// @@ -220,7 +219,8 @@ class SettingsHelper { /// Throws in case of failure static Future setDataConservationPolicy( String password, DataConservationPolicySettings newSettings) async { - await APIRequest(uri: "settings/set_data_conservation_policy", needLogin: true) + await APIRequest( + uri: "settings/set_data_conservation_policy", needLogin: true) .addString("password", password) .addInt("inactive_account_lifetime", newSettings.inactiveAccountLifeTime ?? 0) diff --git a/lib/models/api_request.dart b/lib/models/api_request.dart index 72b5d3d..9a54015 100644 --- a/lib/models/api_request.dart +++ b/lib/models/api_request.dart @@ -4,7 +4,6 @@ import 'package:comunic/helpers/api_helper.dart'; import 'package:comunic/models/api_response.dart'; import 'package:dio/dio.dart'; import 'package:http_parser/http_parser.dart'; -import 'package:image_picker/image_picker.dart'; import 'package:meta/meta.dart'; /// API Request model @@ -32,7 +31,6 @@ class APIRequest { CancelToken cancelToken; Map args; Map files = Map(); - Map pickedFiles = Map(); Map bytesFiles = Map(); APIRequest({@required this.uri, this.needLogin = false, this.args}) @@ -73,11 +71,6 @@ class APIRequest { return this; } - APIRequest addPickedFile(String name, PickedFile file) { - pickedFiles[name] = file; - return this; - } - APIRequest addBytesFile(String name, BytesFile file) { this.bytesFiles[name] = file; return this; diff --git a/lib/models/new_comment.dart b/lib/models/new_comment.dart index 4b2542f..9d86d26 100644 --- a/lib/models/new_comment.dart +++ b/lib/models/new_comment.dart @@ -1,6 +1,7 @@ -import 'package:image_picker/image_picker.dart'; import 'package:meta/meta.dart'; +import 'api_request.dart'; + /// New comment information /// /// @author Pierre HUBERT @@ -8,7 +9,7 @@ import 'package:meta/meta.dart'; class NewComment { final int postID; final String content; - final PickedFile image; + final BytesFile image; const NewComment({ @required this.postID, diff --git a/lib/models/new_emoji.dart b/lib/models/new_emoji.dart index 363d761..db6822f 100644 --- a/lib/models/new_emoji.dart +++ b/lib/models/new_emoji.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; + +import 'api_request.dart'; /// New emoji information /// @@ -7,7 +8,7 @@ import 'package:image_picker/image_picker.dart'; class NewEmoji { final String shortcut; - final PickedFile image; + final BytesFile image; const NewEmoji({ @required this.shortcut, diff --git a/lib/models/new_post.dart b/lib/models/new_post.dart index 91deb27..4f3dd64 100644 --- a/lib/models/new_post.dart +++ b/lib/models/new_post.dart @@ -1,9 +1,10 @@ import 'package:comunic/enums/post_kind.dart'; import 'package:comunic/enums/post_target.dart'; import 'package:comunic/enums/post_visibility_level.dart'; -import 'package:image_picker/image_picker.dart'; import 'package:meta/meta.dart'; +import 'api_request.dart'; + /// New post information /// /// @author Pierre HUBERT @@ -27,7 +28,7 @@ class NewPost { final int targetID; final PostVisibilityLevel visibility; final String content; - final PickedFile image; + final BytesFile image; final String url; final List pdf; final PostKind kind; diff --git a/lib/ui/routes/settings/account_image_settings.dart b/lib/ui/routes/settings/account_image_settings.dart index ab5d7df..33c6817 100644 --- a/lib/ui/routes/settings/account_image_settings.dart +++ b/lib/ui/routes/settings/account_image_settings.dart @@ -15,6 +15,9 @@ import 'package:identicon/identicon.dart'; import 'package:random_string/random_string.dart'; import 'package:settings_ui/settings_ui.dart'; +import '../../../utils/log_utils.dart'; +import '../../../utils/ui_utils.dart'; + /// Account image settings section /// /// @author Pierre Hubert @@ -156,15 +159,16 @@ class _AccountImageSettingsScreenState /// Upload a new account image void _uploadAccountImage() async { - final image = await pickImage(context); + try { + final image = await pickImage(context); - if (image == null) return; + if (image == null) return; - if (!await SettingsHelper.uploadAccountImage(image)) { - showSimpleSnack(context, tr("Could not upload your account image!")); - return; + await SettingsHelper.uploadAccountImage(image); + } catch (e, s) { + logError(e, s); + snack(context, tr("Failed to upload new account image!")); } - _key.currentState.refresh(); } diff --git a/lib/ui/routes/settings/custom_emojies_account_settings.dart b/lib/ui/routes/settings/custom_emojies_account_settings.dart index cb1b5e2..d7a5232 100644 --- a/lib/ui/routes/settings/custom_emojies_account_settings.dart +++ b/lib/ui/routes/settings/custom_emojies_account_settings.dart @@ -12,7 +12,9 @@ import 'package:comunic/utils/input_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; + +import '../../../models/api_request.dart'; +import '../../../utils/ui_utils.dart'; /// Emojies account settings /// @@ -136,7 +138,7 @@ class _NewCustomEmojiDialog extends StatefulWidget { class _NewCustomEmojiDialogState extends State<_NewCustomEmojiDialog> { final _controller = TextEditingController(); - PickedFile _file; + BytesFile _file; bool get _hasImage => _file != null; @@ -209,6 +211,7 @@ class _NewCustomEmojiDialogState extends State<_NewCustomEmojiDialog> { }); } catch (e, stack) { print("Could not pick an image! $e\n$stack"); + snack(context, tr("Failed to pick an image!")); } } } diff --git a/lib/ui/screens/group_settings_screen.dart b/lib/ui/screens/group_settings_screen.dart index 4cf2429..8d7438d 100644 --- a/lib/ui/screens/group_settings_screen.dart +++ b/lib/ui/screens/group_settings_screen.dart @@ -259,19 +259,19 @@ class _GroupSettingsScreenState extends SafeState { // Upload a new logo SettingsTile( title: tr("Upload a new logo"), - onPressed: (_) => _uploadNewLogo, + onPressed: (_) => _uploadNewLogo(), ), // Generate a new random logo SettingsTile( title: tr("Generate a new random logo"), - onPressed: (_) => _generateRandomLogo, + onPressed: (_) => _generateRandomLogo(), ), // Delete current logo SettingsTile( title: tr("Delete logo"), - onPressed: (_) => _deleteLogo, + onPressed: (_) => _deleteLogo(), ), ], ); @@ -281,8 +281,8 @@ class _GroupSettingsScreenState extends SafeState { void _uploadNewLogo() async { try { final logo = await pickImage(context); - final bytes = await logo.readAsBytes(); - await _doUploadLogo(bytes); + if (logo == null) return; + await _doUploadLogo(logo.bytes); } catch (e, stack) { print("Could not upload new logo! $e\n$stack"); showSimpleSnack(context, tr("Could not upload new logo!")); @@ -328,7 +328,7 @@ class _GroupSettingsScreenState extends SafeState { tiles: [ SettingsTile( title: tr("Delete group"), - onPressed: (_) => _deleteGroup, + onPressed: (_) => _deleteGroup(), ), ], ); diff --git a/lib/ui/tiles/post_tile.dart b/lib/ui/tiles/post_tile.dart index 2a54ca7..f7c9b59 100644 --- a/lib/ui/tiles/post_tile.dart +++ b/lib/ui/tiles/post_tile.dart @@ -26,9 +26,11 @@ import 'package:comunic/utils/post_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:image_picker/image_picker.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../../models/api_request.dart'; +import '../../utils/log_utils.dart'; + /// Single posts tile /// /// @author Pierre HUBERT @@ -77,7 +79,7 @@ class _PostTileState extends State { // Class members TextEditingController _commentController = TextEditingController(); - PickedFile _commentImage; + BytesFile _commentImage; bool _submitting = false; int _maxNumberOfCommentToShow = 10; @@ -493,11 +495,16 @@ class _PostTileState extends State { return; } - // Pick a new image - final newImage = await pickImage(context); - setState(() { - _commentImage = newImage; - }); + try { + // Pick a new image + final newImage = await pickImage(context); + setState(() { + _commentImage = newImage; + }); + } catch (e, s) { + logError(e, s); + snack(context, tr("Failed to choose an image!")); + } } /// Submit comment entered by the user diff --git a/lib/ui/widgets/post_create_form_widget.dart b/lib/ui/widgets/post_create_form_widget.dart index 2b5ba53..4025e55 100644 --- a/lib/ui/widgets/post_create_form_widget.dart +++ b/lib/ui/widgets/post_create_form_widget.dart @@ -13,7 +13,10 @@ import 'package:comunic/utils/post_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; + +import '../../models/api_request.dart'; +import '../../utils/log_utils.dart'; +import '../../utils/ui_utils.dart'; /// Widget that allows to create posts /// @@ -48,7 +51,7 @@ class _PostCreateFormWidgetState extends State { bool _isCreating = false; final TextEditingController _postTextController = TextEditingController(); PostVisibilityLevel _postVisibilityLevel; - PickedFile _postImage; + BytesFile _postImage; String _postURL; List _postPDF; DateTime _timeEnd; @@ -239,15 +242,20 @@ class _PostCreateFormWidgetState extends State { /// Pick an image for the new post Future _pickImageForPost() async { - final image = await pickImage(context); + try { + final image = await pickImage(context); - if (image == null) return; + if (image == null) return; - _resetPostSelection(); + _resetPostSelection(); - setState(() { - this._postImage = image; - }); + setState(() { + this._postImage = image; + }); + } catch (e, s) { + logError(e, s); + snack(context, tr("Failed to pick image for post!")); + } } /// Choose a new URL for the post diff --git a/lib/utils/files_utils.dart b/lib/utils/files_utils.dart index b9fa7fb..3e6bb8c 100644 --- a/lib/utils/files_utils.dart +++ b/lib/utils/files_utils.dart @@ -1,54 +1,27 @@ import 'dart:io'; -import 'package:comunic/utils/intl_utils.dart'; import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:random_string/random_string.dart'; +import '../models/api_request.dart'; +import '../ui/dialogs/pick_file_dialog.dart'; + /// Files utilities /// /// @author Pierre HUBERT -enum _ChooseImageSource { GALLERY, CAMERA } - /// Ask the user to choose an image, either from the gallery or using the camera /// -/// Returns null in case of failure -Future pickImage(BuildContext context) async { - /// First, we ask the user to choose between image picker and camera - final result = await showDialog<_ChooseImageSource>( +/// Throws an exception null in case of failure +Future pickImage(BuildContext context) async { + return await showPickFileDialog( context: context, - builder: (c) { - return AlertDialog( - title: Text(tr("Choose an image")), - actions: [ - //Gallery - TextButton( - onPressed: () => Navigator.pop(context, _ChooseImageSource.GALLERY), - child: Text( - tr("Image gallery").toUpperCase(), - ), - ), - - // Camera - TextButton( - onPressed: () => Navigator.pop(context, _ChooseImageSource.CAMERA), - child: Text( - tr("Camera").toUpperCase(), - ), - ), - ], - ); - }, + allowedMimeTypes: ["image/png", "image/jpeg", "image/gif"], + imageMaxHeight: 10000, + imageMaxWidth: 10000, ); - - if (result == null) return null; - return await ImagePicker().getImage( - source: result == _ChooseImageSource.CAMERA - ? ImageSource.camera - : ImageSource.gallery); } /// Generate a new temporary file