From aba1ae22ea446d572a341d0a99acd2263ca59ca5 Mon Sep 17 00:00:00 2001 From: Komposten Date: Fri, 29 Nov 2019 14:39:42 +0100 Subject: [PATCH 01/39] :bug: Fix sharing multiple GIFs from keyboard adding all to the post --- .../home/modals/save_post/create_post.dart | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/pages/home/modals/save_post/create_post.dart b/lib/pages/home/modals/save_post/create_post.dart index 68b01b030..c931d7418 100644 --- a/lib/pages/home/modals/save_post/create_post.dart +++ b/lib/pages/home/modals/save_post/create_post.dart @@ -1,28 +1,29 @@ import 'dart:io'; + import 'package:Okuna/models/community.dart'; import 'package:Okuna/models/post.dart'; import 'package:Okuna/models/post_image.dart'; import 'package:Okuna/models/post_media.dart'; import 'package:Okuna/models/post_video.dart'; +import 'package:Okuna/pages/home/lib/draft_editing_controller.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/create_post_text.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/post_community_previewer.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/post_image_previewer.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/post_video_previewer.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/remaining_post_characters.dart'; -import 'package:Okuna/pages/home/lib/draft_editing_controller.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/draft.dart'; import 'package:Okuna/services/httpie.dart'; import 'package:Okuna/services/link_preview.dart'; -import 'package:Okuna/services/media.dart'; import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/media.dart'; import 'package:Okuna/services/navigation_service.dart'; import 'package:Okuna/services/share.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/user.dart'; import 'package:Okuna/services/validation.dart'; -import 'package:Okuna/widgets/avatars/logged_in_user_avatar.dart'; import 'package:Okuna/widgets/avatars/avatar.dart'; +import 'package:Okuna/widgets/avatars/logged_in_user_avatar.dart'; import 'package:Okuna/widgets/buttons/button.dart'; import 'package:Okuna/widgets/buttons/pill_button.dart'; import 'package:Okuna/widgets/contextual_search_boxes/contextual_search_box_state.dart'; @@ -31,12 +32,12 @@ import 'package:Okuna/widgets/link_preview.dart'; import 'package:Okuna/widgets/nav_bars/themed_nav_bar.dart'; import 'package:Okuna/widgets/new_post_data_uploader.dart'; import 'package:Okuna/widgets/theming/primary_color_container.dart'; +import 'package:Okuna/widgets/theming/smart_text.dart'; import 'package:Okuna/widgets/theming/text.dart'; +import 'package:async/async.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:pigment/pigment.dart'; -import 'package:Okuna/widgets/theming/smart_text.dart'; -import 'package:async/async.dart'; class OBSavePostModal extends StatefulWidget { final Community community; @@ -99,6 +100,7 @@ class OBSavePostModalState extends OBContextualSearchBoxState { bool _saveInProgress; CancelableOperation _saveOperation; + CancelableOperation _gifSharedOperation; @override void initState() { @@ -528,6 +530,11 @@ class OBSavePostModalState extends OBContextualSearchBoxState { } Future _onShare({String text, File image, File video}) async { + if (_gifSharedOperation != null) { + _gifSharedOperation.cancel(); + _gifSharedOperation = null; + } + if (image != null || video != null) { if (_hasImage) { _removePostImageFile(); @@ -550,7 +557,9 @@ class OBSavePostModalState extends OBContextualSearchBoxState { if (video != null) { final isGif = await _mediaService.isGif(video); if (isGif) { - video = await _mediaService.convertGifToVideo(video); + _gifSharedOperation = CancelableOperation.fromFuture( + _mediaService.convertGifToVideo(video)); + video = await _gifSharedOperation.value; } _setPostVideoFile(video); From 3960b227a660bbc9e2c9cd5e86aa03ab982b2cb1 Mon Sep 17 00:00:00 2001 From: Komposten Date: Fri, 31 Jan 2020 14:47:07 +0100 Subject: [PATCH 02/39] :recycle: Re-write share cancelling to be handled by ShareService. The share subscriber must still handle what cancelling the share means. --- .../home/modals/save_post/create_post.dart | 23 ++++++------- lib/services/share.dart | 33 +++++++++++++++---- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/lib/pages/home/modals/save_post/create_post.dart b/lib/pages/home/modals/save_post/create_post.dart index c931d7418..150a72312 100644 --- a/lib/pages/home/modals/save_post/create_post.dart +++ b/lib/pages/home/modals/save_post/create_post.dart @@ -100,7 +100,6 @@ class OBSavePostModalState extends OBContextualSearchBoxState { bool _saveInProgress; CancelableOperation _saveOperation; - CancelableOperation _gifSharedOperation; @override void initState() { @@ -529,12 +528,7 @@ class OBSavePostModalState extends OBContextualSearchBoxState { _addPostItemWidget(postImageWidget); } - Future _onShare({String text, File image, File video}) async { - if (_gifSharedOperation != null) { - _gifSharedOperation.cancel(); - _gifSharedOperation = null; - } - + Future _onShare({String text, File image, File video}) async { if (image != null || video != null) { if (_hasImage) { _removePostImageFile(); @@ -557,12 +551,19 @@ class OBSavePostModalState extends OBContextualSearchBoxState { if (video != null) { final isGif = await _mediaService.isGif(video); if (isGif) { - _gifSharedOperation = CancelableOperation.fromFuture( + var operation = CancelableOperation.fromFuture( _mediaService.convertGifToVideo(video)); - video = await _gifSharedOperation.value; - } - _setPostVideoFile(video); + operation.then((value) { + if (!operation.isCanceled && value != null) { + _setPostVideoFile(value); + } + }); + + return operation; + } else { + _setPostVideoFile(video); + } } return true; diff --git a/lib/services/share.dart b/lib/services/share.dart index dfc0578c7..28afde260 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:async/async.dart'; import 'package:Okuna/plugins/share/share.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/toast.dart'; @@ -20,7 +21,9 @@ class ShareService { StreamSubscription _shareReceiveSubscription; List _shareQueue; - List Function({String text, File image, File video})> _subscribers; + List Function({String text, File image, File video})> _subscribers; + + CancelableOperation _activeShareOperation; BuildContext _context; @@ -58,9 +61,14 @@ class ShareService { /// Subscribe to share events. /// - /// [onShare] should return [true] if it consumes the share. If [false] is - /// returned, the next subscriber will be sent the share as well! - void subscribe(Future Function({String text, File image, File video}) onShare) { + /// [onShare] should return [true] if it consumes the share immediately, + /// a [CancelableOperation] if some amount of work has to be done first (like + /// gif to video conversion in OBSavePostModal), or [false] if the subscriber + /// did _not_ consume the share (the share will be passed on to the next subscriber). + /// + /// If a [CancelableOperation] is returned, it _must_ handle cancellation + /// properly. + void subscribe(Future Function({String text, File image, File video}) onShare) { _subscribers.add(onShare); if (_subscribers.length == 1) { @@ -68,7 +76,7 @@ class ShareService { } } - void unsubscribe(Future Function({String text, File image, File video}) subscriber) { + void unsubscribe(Future Function({String text, File image, File video}) subscriber) { _subscribers.remove(subscriber); } @@ -97,6 +105,11 @@ class ShareService { String text; File image; File video; + + if (_activeShareOperation != null) { + _activeShareOperation.cancel(); + } + if (share.error != null) { _toastService.error( message: _localizationService.trans(share.error), context: _context); @@ -138,7 +151,15 @@ class ShareService { } for (var sub in _subscribers.reversed) { - if (await sub(text: text, image: image, video: video)) { + var subResult = await sub(text: text, image: image, video: video); + + // Stop event propagation if we have a sub-result that is either true or + // a CancelableOperation. + if (subResult is CancelableOperation) { + _activeShareOperation = subResult; + return true; + } + else if (subResult is bool && subResult) { return true; } } From b7afe40f80503e71ba93f504db61bfa212cda8a4 Mon Sep 17 00:00:00 2001 From: Komposten Date: Fri, 31 Jan 2020 18:04:47 +0100 Subject: [PATCH 03/39] :bug: Cancel ffmpeg gif->video conversion on share cancel --- .../home/modals/save_post/create_post.dart | 19 +++++---- lib/services/media.dart | 40 ++++++++++++++----- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/lib/pages/home/modals/save_post/create_post.dart b/lib/pages/home/modals/save_post/create_post.dart index 150a72312..6df622332 100644 --- a/lib/pages/home/modals/save_post/create_post.dart +++ b/lib/pages/home/modals/save_post/create_post.dart @@ -414,9 +414,9 @@ class OBSavePostModalState extends OBContextualSearchBoxState { if (pickedPhoto != null) { bool photoIsGif = await _mediaService.isGif(pickedPhoto); if (photoIsGif) { - File gifVideo = - await _mediaService.convertGifToVideo(pickedPhoto); - _setPostVideoFile(gifVideo); + _mediaService.convertGifToVideo(pickedPhoto).then( + (file) => _setPostVideoFile(file), + onError: (error, trace) => throw error); } else { _setPostImageFile(pickedPhoto); } @@ -551,16 +551,19 @@ class OBSavePostModalState extends OBContextualSearchBoxState { if (video != null) { final isGif = await _mediaService.isGif(video); if (isGif) { - var operation = CancelableOperation.fromFuture( - _mediaService.convertGifToVideo(video)); + var conversionOp = _mediaService.convertGifToVideo(video); - operation.then((value) { - if (!operation.isCanceled && value != null) { + conversionOp.then((value) { + if (!conversionOp.isCanceled && value != null) { _setPostVideoFile(value); } + }, onError: (error, trace) { + if (!conversionOp.isCanceled) { + print(error); + } }); - return operation; + return conversionOp; } else { _setPostVideoFile(video); } diff --git a/lib/services/media.dart b/lib/services/media.dart index 4e7e797f2..c93546811 100644 --- a/lib/services/media.dart +++ b/lib/services/media.dart @@ -1,18 +1,23 @@ +import 'dart:async'; import 'dart:io'; + import 'package:Okuna/plugins/image_converter/image_converter.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/utils_service.dart'; +import 'package:Okuna/services/validation.dart'; +import 'package:async/async.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:meta/meta.dart'; -import 'package:Okuna/services/validation.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:uuid/uuid.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; -import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; + import 'bottom_sheet.dart'; + export 'package:image_picker/image_picker.dart'; class MediaService { @@ -202,26 +207,39 @@ class MediaService { return file; } - Future convertGifToVideo(File gif) async { - File resultFile; - + CancelableOperation convertGifToVideo(File gif) { final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg(); + // Set a cancel flag which we can use if we need to cancel before the ffmpeg + // process is started (can happen for two gif shares with a short time between, + // since we have to wait for _getTempPath() before we can start ffmpeg. + var isCancelled = false; + var ffmpegFuture = + _convertGifToVideo(_flutterFFmpeg, gif, () => isCancelled); + return CancelableOperation.fromFuture(ffmpegFuture, onCancel: () { + isCancelled = true; + _flutterFFmpeg.cancel(); + }); + } + + Future _convertGifToVideo( + FlutterFFmpeg flutterFFmpeg, File gif, bool Function() doCancel) async { String resultFileName = _uuid.v4() + '.mp4'; final path = await _getTempPath(); final String sourceFilePath = gif.path; final String resultFilePath = '$path/$resultFileName'; - int exitCode = await _flutterFFmpeg.execute( - '-f gif -i $sourceFilePath -pix_fmt yuv420p -c:v libx264 -movflags +faststart -filter:v crop=\'floor(in_w/2)*2:floor(in_h/2)*2\' $resultFilePath'); + var exitCode; + if (!doCancel()) { + exitCode = await flutterFFmpeg.execute( + '-f gif -i $sourceFilePath -pix_fmt yuv420p -c:v libx264 -movflags +faststart -filter:v crop=\'floor(in_w/2)*2:floor(in_h/2)*2\' $resultFilePath'); + } if (exitCode == 0) { - resultFile = File(resultFilePath); + return File(resultFilePath); } else { - throw (Exception('Gif couldn\'t be converted to video')); + throw 'Gif couldn\'t be converted to video'; } - - return resultFile; } void clearThumbnailForFile(File videoFile) { From 0cc7c19cf18fb06a86f88cebd442318f5dee64bc Mon Sep 17 00:00:00 2001 From: Komposten Date: Fri, 31 Jan 2020 18:11:27 +0100 Subject: [PATCH 04/39] :bug: ShareService should not queue multiple shares before a sub is added --- lib/services/share.dart | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/services/share.dart b/lib/services/share.dart index 28afde260..9fdbcd7bf 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -20,15 +20,14 @@ class ShareService { LocalizationService _localizationService; StreamSubscription _shareReceiveSubscription; - List _shareQueue; List Function({String text, File image, File video})> _subscribers; + Share _queuedShare; CancelableOperation _activeShareOperation; BuildContext _context; ShareService() { - _shareQueue = []; _subscribers = []; if (Platform.isAndroid) { @@ -81,21 +80,16 @@ class ShareService { } Future _emptyQueue() async { - var consumed = []; - for (Share share in _shareQueue) { - if (await _onShare(share)) { - consumed.add(share); - } - } - - consumed.forEach((e) => _shareQueue.remove(e)); + var share = _queuedShare; + _queuedShare = null; + await _onShare(share); } void _onReceiveShare(dynamic shared) async { var share = Share.fromReceived(shared); if (_subscribers.isEmpty) { - _shareQueue.add(share); + _queuedShare = share; } else { await _onShare(share); } From 8d8a0c82f17e2dc155b49de21dc1a84f6e4a1e3f Mon Sep 17 00:00:00 2001 From: Komposten Date: Fri, 31 Jan 2020 18:27:53 +0100 Subject: [PATCH 05/39] :bug: Don't process a new share until the previous one is done/cancelled --- lib/services/share.dart | 65 +++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/lib/services/share.dart b/lib/services/share.dart index 9fdbcd7bf..30bfee379 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'dart:io'; -import 'package:async/async.dart'; import 'package:Okuna/plugins/share/share.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/validation.dart'; +import 'package:async/async.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -20,9 +20,11 @@ class ShareService { LocalizationService _localizationService; StreamSubscription _shareReceiveSubscription; - List Function({String text, File image, File video})> _subscribers; + List Function({String text, File image, File video})> + _subscribers; Share _queuedShare; + bool _isProcessingShare = false; CancelableOperation _activeShareOperation; BuildContext _context; @@ -67,41 +69,51 @@ class ShareService { /// /// If a [CancelableOperation] is returned, it _must_ handle cancellation /// properly. - void subscribe(Future Function({String text, File image, File video}) onShare) { + void subscribe( + Future Function({String text, File image, File video}) onShare) { _subscribers.add(onShare); if (_subscribers.length == 1) { - _emptyQueue(); + _processQueuedShare(); } } - void unsubscribe(Future Function({String text, File image, File video}) subscriber) { + void unsubscribe( + Future Function({String text, File image, File video}) + subscriber) { _subscribers.remove(subscriber); } - Future _emptyQueue() async { - var share = _queuedShare; - _queuedShare = null; - await _onShare(share); + void _onReceiveShare(dynamic shared) async { + _queuedShare = Share.fromReceived(shared); + + if (_subscribers.isNotEmpty && !_isProcessingShare) { + _processQueuedShare(); + } } - void _onReceiveShare(dynamic shared) async { - var share = Share.fromReceived(shared); + Future _processQueuedShare() async { + if (_queuedShare != null) { + var share = _queuedShare; + _queuedShare = null; - if (_subscribers.isEmpty) { - _queuedShare = share; - } else { + _isProcessingShare = true; await _onShare(share); + _isProcessingShare = false; + + // Recurse since a new share might have came in while the last was being processed. + _processQueuedShare(); } } - Future _onShare(Share share) async { + Future _onShare(Share share) async { String text; File image; File video; if (_activeShareOperation != null) { _activeShareOperation.cancel(); + _activeShareOperation = null; } if (share.error != null) { @@ -110,7 +122,7 @@ class ShareService { if (share.error.contains('uri_scheme')) { throw share.error; } - return true; + return; } if (share.image != null) { @@ -120,7 +132,7 @@ class ShareService { image, OBImageType.post)) { _showFileTooLargeToast( _validationService.getAllowedImageSize(OBImageType.post)); - return true; + return; } } @@ -129,7 +141,7 @@ class ShareService { if (!await _validationService.isVideoAllowedSize(video)) { _showFileTooLargeToast(_validationService.getAllowedVideoSize()); - return true; + return; } } @@ -140,7 +152,7 @@ class ShareService { message: 'Text too long (limit: ${ValidationService.POST_MAX_LENGTH} characters)', context: _context); - return true; + return; } } @@ -151,20 +163,17 @@ class ShareService { // a CancelableOperation. if (subResult is CancelableOperation) { _activeShareOperation = subResult; - return true; - } - else if (subResult is bool && subResult) { - return true; + break; + } else if (subResult == true) { + break; } } - - return false; } Future _showFileTooLargeToast(int limitInBytes) async { _toastService.error( - message: _localizationService.image_picker__error_too_large( - limitInBytes ~/ 1048576), + message: _localizationService + .image_picker__error_too_large(limitInBytes ~/ 1048576), context: _context); } -} \ No newline at end of file +} From 55b57c1e303ac3c426b61fa4039e7b0df251c594 Mon Sep 17 00:00:00 2001 From: Komposten Date: Fri, 31 Jan 2020 18:30:21 +0100 Subject: [PATCH 06/39] :bug: Fix exception if post modal is closed while converting gif Previously setState was called after the gif conversion finished even if the modal had been disposed. --- lib/pages/home/modals/save_post/create_post.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/pages/home/modals/save_post/create_post.dart b/lib/pages/home/modals/save_post/create_post.dart index 6df622332..6bb0efe51 100644 --- a/lib/pages/home/modals/save_post/create_post.dart +++ b/lib/pages/home/modals/save_post/create_post.dart @@ -460,6 +460,10 @@ class OBSavePostModalState extends OBContextualSearchBoxState { } void _setPostImageFile(File image) { + if (!mounted) { + return; + } + setState(() { this._postImageFile = image; _hasImage = true; @@ -486,6 +490,10 @@ class OBSavePostModalState extends OBContextualSearchBoxState { } void _setPostVideoFile(File video) { + if (!mounted) { + return; + } + setState(() { this._postVideoFile = video; _hasVideo = true; From a61c3eb078108db996dec8cb0f60086ee467a30c Mon Sep 17 00:00:00 2001 From: Shantanu Date: Sat, 22 Feb 2020 13:27:16 +0100 Subject: [PATCH 07/39] :sparkles: user versioning for trending posts n communities --- lib/services/communities_api.dart | 3 ++- lib/services/posts_api.dart | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/services/communities_api.dart b/lib/services/communities_api.dart index 36e44cfac..8e441a12c 100644 --- a/lib/services/communities_api.dart +++ b/lib/services/communities_api.dart @@ -116,7 +116,8 @@ class CommunitiesApiService { return _httpService.get('$apiURL$GET_TRENDING_COMMUNITIES_PATH', queryParameters: queryParams, - appendAuthorizationToken: authenticatedRequest); + appendAuthorizationToken: authenticatedRequest, + headers: {'Accept': 'application/json; version=2.0'}); } Future getSuggestedCommunities( diff --git a/lib/services/posts_api.dart b/lib/services/posts_api.dart index 1e5053771..fd7853fa4 100644 --- a/lib/services/posts_api.dart +++ b/lib/services/posts_api.dart @@ -24,7 +24,7 @@ class PostsApiService { 'api/posts/profile/excluded-communities/{communityName}/'; static const EXCLUDED_PROFILE_POSTS_COMMUNITIES_SEARCH_PATH = 'api/posts/profile/excluded-communities/search/'; - static const GET_TRENDING_POSTS_PATH = 'api/posts/trending/new/'; + static const GET_TRENDING_POSTS_PATH = 'api/posts/trending/'; static const CREATE_POST_PATH = 'api/posts/'; static const POST_MEDIA_PATH = 'api/posts/{postUuid}/media/'; static const EDIT_POST_PATH = 'api/posts/{postUuid}/'; @@ -126,7 +126,8 @@ class PostsApiService { return _httpService.get('$apiURL$GET_TRENDING_POSTS_PATH', queryParameters: queryParams, - appendAuthorizationToken: authenticatedRequest); + appendAuthorizationToken: authenticatedRequest, + headers: {'Accept': 'application/json; version=2.0'}); } Future getTimelinePosts( From f8a7e107cbb7050b7f3500c7b46cb1c46fe55269 Mon Sep 17 00:00:00 2001 From: Shantanu Date: Mon, 24 Feb 2020 13:36:19 +0100 Subject: [PATCH 08/39] :sparkles: add readonly option to text form field --- lib/widgets/fields/text_form_field.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/widgets/fields/text_form_field.dart b/lib/widgets/fields/text_form_field.dart index b8e25c502..2b1d870d9 100644 --- a/lib/widgets/fields/text_form_field.dart +++ b/lib/widgets/fields/text_form_field.dart @@ -8,6 +8,7 @@ class OBTextFormField extends StatelessWidget { final TextEditingController controller; final FormFieldValidator validator; final bool autofocus; + final bool readOnly; final InputDecoration decoration; final int maxLines; final TextInputType keyboardType; @@ -25,6 +26,7 @@ class OBTextFormField extends StatelessWidget { {this.controller, this.validator, this.autofocus = false, + this.readOnly = false, this.keyboardType, this.inputFormatters, this.obscureText = false, @@ -85,6 +87,7 @@ class OBTextFormField extends StatelessWidget { textCapitalization: textCapitalization, textInputAction: textInputAction, autofocus: autofocus, + readOnly: readOnly, controller: controller, validator: validator, keyboardType: keyboardType, From 5e4e0742836cb1cc3003389a5130e956577464d5 Mon Sep 17 00:00:00 2001 From: Shantanu Date: Mon, 24 Feb 2020 13:36:53 +0100 Subject: [PATCH 09/39] :sparkles: show current email in change email screen --- .../modals/change_email/change_email.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/pages/home/pages/menu/pages/settings/pages/account_settings/modals/change_email/change_email.dart b/lib/pages/home/pages/menu/pages/settings/pages/account_settings/modals/change_email/change_email.dart index 0e72a4f6e..57338f503 100644 --- a/lib/pages/home/pages/menu/pages/settings/pages/account_settings/modals/change_email/change_email.dart +++ b/lib/pages/home/pages/menu/pages/settings/pages/account_settings/modals/change_email/change_email.dart @@ -36,6 +36,7 @@ class OBChangeEmailModalState extends State { bool _changedEmailTaken = false; bool _formValid = true; TextEditingController _emailController = TextEditingController(); + TextEditingController _currentEmailController; CancelableOperation _requestOperation; @override @@ -61,6 +62,9 @@ class OBChangeEmailModalState extends State { _toastService = openbookProvider.toastService; _userService = openbookProvider.userService; _localizationService = openbookProvider.localizationService; + + String currentUserEmail = _userService.getLoggedInUser().getEmail(); + _currentEmailController = TextEditingController(text: currentUserEmail); return OBCupertinoPageScaffold( navigationBar: _buildNavigationBar(), @@ -72,6 +76,15 @@ class OBChangeEmailModalState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + OBTextFormField( + size: OBTextFormFieldSize.large, + autofocus: false, + readOnly: true, + controller: _currentEmailController, + decoration: InputDecoration( + labelText:_localizationService.user__change_email_current_email_text, + ), + ), OBTextFormField( size: OBTextFormFieldSize.large, autofocus: true, From d0e31d5959574e5bc9c3d593bbc11aa9194c42fd Mon Sep 17 00:00:00 2001 From: Shantanu Date: Mon, 24 Feb 2020 13:38:16 +0100 Subject: [PATCH 10/39] :globe_with_meridians: localization text --- assets/i18n/en/user.arb | 65 +++++++++++++++++++++++++++++++--- lib/services/localization.dart | 4 +++ 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/assets/i18n/en/user.arb b/assets/i18n/en/user.arb index a2bc55b90..c52c41f92 100644 --- a/assets/i18n/en/user.arb +++ b/assets/i18n/en/user.arb @@ -298,18 +298,68 @@ "type": "text", "placeholders": {} }, - "edit_profile_followers_count": "Followers count", - "@edit_profile_followers_count": { + "manage_profile_followers_count_toggle": "Followers count", + "@manage_profile_followers_count_toggle": { "type": "text", "placeholders": {} }, - "edit_profile_community_posts": "Community posts", + "manage_profile_followers_count_toggle__descr": "Display the number of people that follow you, on your profile.", + "@manage_profile_followers_count_toggle__descr": { + "type": "text", + "placeholders": {} + }, + "manage_profile_community_posts_toggle": "Community posts", + "@manage_profile_community_posts_toggle": { + "type": "text", + "placeholders": {} + }, + "manage_profile_community_posts_toggle__descr": "Display posts you share with public communities, on your profile.", + "@manage_profile_community_posts_toggle__descr": { + "type": "text", + "placeholders": {} + }, + "edit_profile_community_posts": "Communities", "@edit_profile_community_posts": { "type": "text", "placeholders": {} }, - "edit_profile_title": "Edit profile", - "@edit_profile_title": { + "edit_profile_community_posts_descr": "Display in profile", + "@edit_profile_community_posts_descr": { + "type": "text", + "placeholders": {} + }, + "manage": "Manage", + "@manage": { + "type": "text", + "placeholders": {} + }, + "manage_profile_title": "Manage profile", + "@manage_profile_title": { + "type": "text", + "placeholders": {} + }, + "manage_profile_details_title": "Details", + "@manage_profile_details_title": { + "type": "text", + "placeholders": {} + }, + "manage_profile_details_title_desc": "Change your username, name, url, location, avatar or cover photo.", + "@manage_profile_details_title_desc": { + "type": "text", + "placeholders": {} + }, + "profile_posts_excluded_communities": "Hidden communities", + "@profile_posts_excluded_communities": { + "type": "text", + "placeholders": {} + }, + "profile_posts_exclude_communities": "Exclude communities", + "@profile_posts_exclude_communities": { + "type": "text", + "placeholders": {} + }, + "profile_posts_excluded_communities_desc": "See, add and remove hidden communities from your profile.", + "@profile_posts_excluded_communities_desc": { "type": "text", "placeholders": {} }, @@ -924,6 +974,11 @@ "type": "text", "placeholders": {} }, + "change_email_current_email_text": "Current email", + "@change_email_current_email_text": { + "type": "text", + "placeholders": {} + }, "change_email_hint_text": "Enter your new email", "@change_email_hint_text": { "type": "text", diff --git a/lib/services/localization.dart b/lib/services/localization.dart index 047084989..bf6dc7298 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -3209,6 +3209,10 @@ class LocalizationService { return Intl.message("Email", name: 'user__change_email_email_text'); } + String get user__change_email_current_email_text { + return Intl.message("Current email", name: 'user__change_email_current_email_text'); + } + String get user__change_email_hint_text { return Intl.message("Enter your new email", name: 'user__change_email_hint_text'); From b3782a4c9b06083a7fb6d852d28472cbf0c98a81 Mon Sep 17 00:00:00 2001 From: Shantanu Date: Wed, 15 Apr 2020 19:14:26 +0200 Subject: [PATCH 11/39] :sparkles: model n api service --- .../post_notifications_subscription.dart | 73 +++++++++++++++++++ lib/services/posts_api.dart | 47 ++++++++++++ lib/services/user.dart | 37 ++++++++++ 3 files changed, 157 insertions(+) create mode 100644 lib/models/post_notifications_subscription.dart diff --git a/lib/models/post_notifications_subscription.dart b/lib/models/post_notifications_subscription.dart new file mode 100644 index 000000000..e39ba1ed2 --- /dev/null +++ b/lib/models/post_notifications_subscription.dart @@ -0,0 +1,73 @@ +import 'package:Okuna/models/updatable_model.dart'; +import 'package:dcache/dcache.dart'; + +class PostNotificationsSubscription + extends UpdatableModel { + final int id; + final String postUuid; + bool commentNotifications; + bool reactionNotifications; + bool replyNotifications; + + PostNotificationsSubscription( + {this.id, + this.postUuid, + this.commentNotifications, + this.reactionNotifications, + this.replyNotifications}); + + static final factory = PostNotificationsSubscriptionFactory(); + + factory PostNotificationsSubscription.fromJSON(Map json) { + if (json == null) return null; + return factory.fromJson(json); + } + + Map toJson() { + return { + 'id': id, + 'post_uuid': postUuid, + 'comment_notifications': commentNotifications, + 'reaction_notifications': reactionNotifications, + 'reply_notifications': replyNotifications, + }; + } + + Map getSettingsObject() { + return { + 'commentNotifications': this.commentNotifications, + 'replyNotifications': this.replyNotifications, + 'reactionNotifications': this.reactionNotifications + }; + } + + @override + void updateFromJson(Map json) { + if (json.containsKey('comment_notifications')) { + commentNotifications = json['comment_notifications']; + } + if (json.containsKey('reaction_notifications')) { + reactionNotifications = json['reaction_notifications']; + } + if (json.containsKey('reply_notifications')) { + replyNotifications = json['reply_notifications']; + } + } +} + +class PostNotificationsSubscriptionFactory + extends UpdatableModelFactory { + @override + SimpleCache cache = + SimpleCache(storage: UpdatableModelSimpleStorage(size: 20)); + + @override + PostNotificationsSubscription makeFromJson(Map json) { + return PostNotificationsSubscription( + id: json['id'], + postUuid: json['post_uuid'], + commentNotifications: json['comment_notifications'], + reactionNotifications: json['reaction_notifications'], + replyNotifications: json['reply_notifications']); + } +} diff --git a/lib/services/posts_api.dart b/lib/services/posts_api.dart index 1e5053771..a56accbb3 100644 --- a/lib/services/posts_api.dart +++ b/lib/services/posts_api.dart @@ -42,6 +42,8 @@ class PostsApiService { 'api/posts/{postUuid}/comments/{postCommentId}/replies/'; static const MUTE_POST_PATH = 'api/posts/{postUuid}/notifications/mute/'; static const UNMUTE_POST_PATH = 'api/posts/{postUuid}/notifications/unmute/'; + static const POST_NOTIFICATIONS_SUBSCRIPTION_PATH = + 'api/posts/{postUuid}/notifications/subscribe/'; static const REPORT_POST_PATH = 'api/posts/{postUuid}/report/'; static const PREVIEW_POST_DATA_PATH = 'api/posts/{postUuid}/link-preview/'; static const TRANSLATE_POST_PATH = 'api/posts/{postUuid}/translate/'; @@ -436,6 +438,46 @@ class PostsApiService { return _httpService.post(_makeApiUrl(path), appendAuthorizationToken: true); } + Future createPostNotificationsSubscription( + {@required String postUuid, + bool commentNotifications, + bool reactionNotifications, + bool replyNotifications}) { + Map body = {}; + if (commentNotifications != null) { + body['comment_notifications'] = commentNotifications; + } + if (reactionNotifications != null) { + body['reaction_notifications'] = reactionNotifications; + } + if (replyNotifications != null) { + body['reply_notifications'] = replyNotifications; + } + String path = _makePostNotificationsSubscriptionPath(postUuid); + return _httpService.putJSON(_makeApiUrl(path), + body: body, appendAuthorizationToken: true); + } + + Future updatePostNotificationsSubscription( + {@required String postUuid, + bool commentNotifications, + bool reactionNotifications, + bool replyNotifications}) { + Map body = {}; + if (commentNotifications != null) { + body['comment_notifications'] = commentNotifications; + } + if (reactionNotifications != null) { + body['reaction_notifications'] = reactionNotifications; + } + if (replyNotifications != null) { + body['reply_notifications'] = replyNotifications; + } + String path = _makePostNotificationsSubscriptionPath(postUuid); + return _httpService.patchJSON(_makeApiUrl(path), + body: body, appendAuthorizationToken: true); + } + Future disableCommentsForPostWithUuidPost(String postUuid) { String path = _makeDisableCommentsForPostPath(postUuid); return _httpService.post(_makeApiUrl(path), appendAuthorizationToken: true); @@ -629,6 +671,11 @@ class PostsApiService { .parse(UNMUTE_POST_PATH, {'postUuid': postUuid}); } + String _makePostNotificationsSubscriptionPath(String postUuid) { + return _stringTemplateService + .parse(POST_NOTIFICATIONS_SUBSCRIPTION_PATH, {'postUuid': postUuid}); + } + String _makeMutePostCommentPath({ @required int postCommentId, @required String postUuid, diff --git a/lib/services/user.dart b/lib/services/user.dart index f470d4bc7..c7c089e6a 100644 --- a/lib/services/user.dart +++ b/lib/services/user.dart @@ -36,6 +36,7 @@ import 'package:Okuna/models/post_comment_list.dart'; import 'package:Okuna/models/post_comment_reaction.dart'; import 'package:Okuna/models/post_comment_reaction_list.dart'; import 'package:Okuna/models/post_media_list.dart'; +import 'package:Okuna/models/post_notifications_subscription.dart'; import 'package:Okuna/models/post_reaction.dart'; import 'package:Okuna/models/post_reaction_list.dart'; import 'package:Okuna/models/reactions_emoji_count_list.dart'; @@ -765,6 +766,9 @@ class UserService { HttpieResponse response = await _postsApiService.commentPost(postUuid: post.uuid, text: text); _checkResponseIsCreated(response); + // refresh post to get notifications subscription model + if (post.postNotificationsSubscription == null) + await getPostWithUuid(post.uuid); return PostComment.fromJSON(json.decode(response.body)); } @@ -793,6 +797,9 @@ class UserService { HttpieResponse response = await _postsApiService.replyPostComment( postUuid: post.uuid, postCommentId: postComment.id, text: text); _checkResponseIsCreated(response); + // refresh post to get notifications subscription model + if (post.postNotificationsSubscription == null) + await getPostWithUuid(post.uuid); return PostComment.fromJSON(json.decode(response.body)); } @@ -817,6 +824,36 @@ class UserService { return Post.fromJson(json.decode(response.body)); } + Future createPostNotificationsSubscription( + {@required Post post, + bool commentNotifications, + bool reactionNotifications, + bool replyNotifications}) async { + HttpieResponse response = + await _postsApiService.createPostNotificationsSubscription( + postUuid: post.uuid, + commentNotifications: commentNotifications, + reactionNotifications: reactionNotifications, + replyNotifications: replyNotifications); + _checkResponseIsCreated(response); + return Post.fromJson(json.decode(response.body)); + } + + Future updatePostNotificationsSubscription( + {@required Post post, + bool commentNotifications, + bool reactionNotifications, + bool replyNotifications}) async { + HttpieResponse response = + await _postsApiService.updatePostNotificationsSubscription( + postUuid: post.uuid, + commentNotifications: commentNotifications, + reactionNotifications: reactionNotifications, + replyNotifications: replyNotifications); + _checkResponseIsOk(response); + return Post.fromJson(json.decode(response.body)); + } + Future excludeCommunityFromTopPosts(Community community) async { HttpieResponse response = await _postsApiService .excludeCommunityFromTopPosts(communityName: community.name); From a14b965133fa583fd32de6442f78770f58594ef8 Mon Sep 17 00:00:00 2001 From: Shantanu Date: Wed, 15 Apr 2020 19:15:38 +0200 Subject: [PATCH 12/39] :sparkles: add manange notifications tile widgets --- lib/models/post.dart | 24 +- lib/models/user.dart | 11 + .../home/bottom_sheets/post_actions.dart | 27 ++- .../manage_post_notifications.dart | 224 ++++++++++++++++++ .../widgets/manage_notifications.dart | 183 ++++++++++++++ lib/services/localization.dart | 96 ++++++++ lib/services/modal_service.dart | 20 ++ lib/widgets/fields/toggle_field.dart | 12 +- .../manage_post_notifications_tile.dart | 79 ++++++ 9 files changed, 662 insertions(+), 14 deletions(-) create mode 100644 lib/pages/home/modals/manage_notifications/manage_post_notifications.dart create mode 100644 lib/pages/home/modals/manage_notifications/widgets/manage_notifications.dart create mode 100644 lib/widgets/tiles/actions/manage_post_notifications_tile.dart diff --git a/lib/models/post.dart b/lib/models/post.dart index 46dc35254..5ae221d02 100644 --- a/lib/models/post.dart +++ b/lib/models/post.dart @@ -8,6 +8,7 @@ import 'package:Okuna/models/post_comment.dart'; import 'package:Okuna/models/post_comment_list.dart'; import 'package:Okuna/models/post_media.dart'; import 'package:Okuna/models/post_media_list.dart'; +import 'package:Okuna/models/post_notifications_subscription.dart'; import 'package:Okuna/models/post_preview_link_data.dart'; import 'package:Okuna/models/post_reaction.dart'; import 'package:Okuna/models/reactions_emoji_count.dart'; @@ -26,6 +27,7 @@ class Post extends UpdatableModel { DateTime created; User creator; CirclesList circles; + PostNotificationsSubscription postNotificationsSubscription; ReactionsEmojiCountList reactionsEmojiCounts; PostReaction reaction; @@ -102,7 +104,8 @@ class Post extends UpdatableModel { 'is_encircled': isEncircled, 'is_edited': isEdited, 'is_closed': isClosed, - 'is_reported': isReported + 'is_reported': isReported, + 'post_notifications_subscription': postNotificationsSubscription }; } @@ -129,6 +132,7 @@ class Post extends UpdatableModel { this.reaction, this.reactionsEmojiCounts, this.areCommentsEnabled, + this.postNotificationsSubscription, this.circles, this.community, this.status, @@ -211,6 +215,11 @@ class Post extends UpdatableModel { if (json.containsKey('circles')) circles = factory.parseCircles(json['circles']); + + if (json.containsKey('post_notifications_subscription')) + postNotificationsSubscription = + factory.parsePostNotificationsSubscription( + json['post_notifications_subscription']); } void updatePreviewDataFromJson(Map json) { @@ -316,6 +325,10 @@ class Post extends UpdatableModel { return creator.id; } + PostNotificationsSubscription getNotificationsSubscription() { + return this.postNotificationsSubscription; + } + String getCreatorAvatar() { return creator.profile.avatar; } @@ -466,6 +479,8 @@ class PostFactory extends UpdatableModelFactory { isEncircled: json['is_encircled'], isEdited: json['is_edited'], isClosed: json['is_closed'], + postNotificationsSubscription: parsePostNotificationsSubscription( + json['post_notifications_subscription']), reactionsEmojiCounts: parseReactionsEmojiCounts(json['reactions_emoji_counts'])); } @@ -520,6 +535,13 @@ class PostFactory extends UpdatableModelFactory { return CirclesList.fromJson(circlesData); } + PostNotificationsSubscription parsePostNotificationsSubscription( + Map postNotificationsSubscriptionData) { + if (postNotificationsSubscriptionData == null) return null; + return PostNotificationsSubscription.fromJSON( + postNotificationsSubscriptionData); + } + Language parseLanguage(Map languageData) { if (languageData == null) return null; return Language.fromJson(languageData); diff --git a/lib/models/user.dart b/lib/models/user.dart index 0edb499ce..e12e77565 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -410,6 +410,17 @@ class User extends UpdatableModel { notifyUpdate(); } + bool canEnablePostSubscriptionNotifications(Post post) { + User loggedInUser = this; + bool _canDisableOrEnablePostSubscriptionComments = true; + + if (post.getCreatorId() == loggedInUser.id) { + _canDisableOrEnablePostSubscriptionComments = false; + } + + return _canDisableOrEnablePostSubscriptionComments; + } + bool canDisableOrEnableCommentsForPost(Post post) { User loggedInUser = this; bool _canDisableOrEnableComments = false; diff --git a/lib/pages/home/bottom_sheets/post_actions.dart b/lib/pages/home/bottom_sheets/post_actions.dart index 16d0e9ccf..d6a7fdeff 100644 --- a/lib/pages/home/bottom_sheets/post_actions.dart +++ b/lib/pages/home/bottom_sheets/post_actions.dart @@ -16,6 +16,7 @@ import 'package:Okuna/widgets/tiles/actions/close_post_tile.dart'; import 'package:Okuna/widgets/tiles/actions/disable_comments_post_tile.dart'; import 'package:Okuna/widgets/tiles/actions/exclude_community_from_profile_posts_tile.dart'; import 'package:Okuna/widgets/tiles/actions/exclude_community_from_top_posts_tile.dart'; +import 'package:Okuna/widgets/tiles/actions/manage_post_notifications_tile.dart'; import 'package:Okuna/widgets/tiles/actions/mute_post_tile.dart'; import 'package:Okuna/widgets/tiles/actions/report_post_tile.dart'; import 'package:flutter/cupertino.dart'; @@ -96,19 +97,25 @@ class OBPostActionsBottomSheetState extends State { widget.onPostCommunityExcludedFromProfilePosts)); } - postActions.add(OBMutePostTile( +// postActions.add(OBMutePostTile( +// post: post, +// onMutedPost: _dismiss, +// onUnmutedPost: _dismiss, +// )); + + postActions.add(OBManagePostNotificationsTile( post: post, - onMutedPost: _dismiss, - onUnmutedPost: _dismiss, + onOpenManagePostNotificationsModal: _dismiss, + onNotificationSettingsSave: _dismiss, )); - if (loggedInUser.canDisableOrEnableCommentsForPost(post)) { - postActions.add(OBDisableCommentsPostTile( - post: post, - onDisableComments: _dismiss, - onEnableComments: _dismiss, - )); - } +// if (loggedInUser.canDisableOrEnableCommentsForPost(post)) { +// postActions.add(OBDisableCommentsPostTile( +// post: post, +// onDisableComments: _dismiss, +// onEnableComments: _dismiss, +// )); +// } if (loggedInUser.canCloseOrOpenPost(post)) { postActions.add(OBClosePostTile( diff --git a/lib/pages/home/modals/manage_notifications/manage_post_notifications.dart b/lib/pages/home/modals/manage_notifications/manage_post_notifications.dart new file mode 100644 index 000000000..0de4ad66a --- /dev/null +++ b/lib/pages/home/modals/manage_notifications/manage_post_notifications.dart @@ -0,0 +1,224 @@ +import 'package:Okuna/models/post.dart'; +import 'package:Okuna/models/post_notifications_subscription.dart'; +import 'package:Okuna/pages/home/modals/manage_notifications/widgets/manage_notifications.dart'; +import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/provider.dart'; +import 'package:Okuna/services/toast.dart'; +import 'package:Okuna/services/user.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:async/async.dart'; + +class OBManagePostNotificationsModal extends StatefulWidget { + final Post post; + final OnNotificationSettingsSave onNotificationSettingsSave; + + OBManagePostNotificationsModal( + {Key key, this.onNotificationSettingsSave, this.post}) + : super(key: key); + + @override + OBManagePostNotificationsModalState createState() { + return OBManagePostNotificationsModalState(); + } +} + +class OBManagePostNotificationsModalState + extends State { + UserService _userService; + LocalizationService _localizationService; + ToastService _toastService; + PostNotificationsSubscription _postNotificationsSubscription; + CancelableOperation _saveOperation; + CancelableOperation _muteOperation; + + @override + void initState() { + super.initState(); + _postNotificationsSubscription = widget.post.postNotificationsSubscription; + } + + @override + Widget build(BuildContext context) { + var openbookProvider = OpenbookProvider.of(context); + _userService = openbookProvider.userService; + _localizationService = openbookProvider.localizationService; + _toastService = openbookProvider.toastService; + + return OBManageNotifications( + notificationSettings: _getNotificationSettingsList(), + onWantsToSaveSettings: _onWantsToSaveSettings, + isMuted: widget.post.isMuted, + onWantsToToggleMute: _onWantsToToggleMute, + onDismissModal: _onDismiss); + } + + void _onDismiss() { + if (_saveOperation != null) _saveOperation.cancel(); + if (_muteOperation != null) _muteOperation.cancel(); + if (widget.onNotificationSettingsSave != null) { + widget.onNotificationSettingsSave(); + } + } + + List _getNotificationSettingsList() { + List _notificationSettings = [ + PostNotificationSetting( + key: PostNotificationSetting.COMMENT_NOTIFICATIONS, + value: _postNotificationsSubscription?.commentNotifications ?? false, + isDisabled: widget.post.isMuted, + localizedTitle: + _localizationService.post__manage_notifications_comments_title, + localizedDesc: + _localizationService.post__manage_notifications_comments_desc), + PostNotificationSetting( + key: PostNotificationSetting.REPLY_NOTIFICATIONS, + value: _postNotificationsSubscription?.replyNotifications ?? false, + isDisabled: widget.post.isMuted, + localizedTitle: + _localizationService.post__manage_notifications_replies_title, + localizedDesc: + _localizationService.post__manage_notifications_replies_desc), + ]; + // add post reaction setting if user is creator + if (widget.post.creator == _userService.getLoggedInUser()) { + _notificationSettings.insert( + 0, + PostNotificationSetting( + key: PostNotificationSetting.REACTION_NOTIFICATIONS, + value: _postNotificationsSubscription?.reactionNotifications ?? + false, + isDisabled: widget.post.isMuted, + localizedTitle: _localizationService + .post__manage_notifications_reactions_title, + localizedDesc: _localizationService + .post__manage_notifications_reactions_desc)); + } + return _notificationSettings; + } + + void _onWantsToToggleMute() async { + try { + if (!widget.post.isMuted) { + _muteOperation = + CancelableOperation.fromFuture(_userService.mutePost(widget.post)); + await _muteOperation.value; + _toastService.success( + message: _localizationService + .post__manage_notifications_successfully_muted, + context: context); + } else { + _muteOperation = CancelableOperation.fromFuture( + _userService.unmutePost(widget.post)); + await _muteOperation.value; + _toastService.success( + message: _localizationService + .post__manage_notifications_successfully_unmuted, + context: context); + } + } catch (error) { + _onError(error); + } + } + + void _onWantsToSaveSettings( + Map notificationSettingsMap) async { + try { + if (widget.post.postNotificationsSubscription == null) { + _saveOperation = CancelableOperation.fromFuture( + _createUserPostNotificationsSubscription(notificationSettingsMap)); + await _saveOperation.value; + } else { + _saveOperation = CancelableOperation.fromFuture( + _updateUserPostNotificationsSubscription(notificationSettingsMap)); + await _saveOperation.value; + } + _toastService.success( + message: _localizationService + .post__manage_notifications_successfully_saved, + context: context); + } catch (error) { + _onError(error); + } + if (widget.onNotificationSettingsSave != null) + widget.onNotificationSettingsSave(); + Navigator.pop(context); + } + + void _onError(error) async { + if (error is HttpieConnectionRefusedError) { + _toastService.error( + message: error.toHumanReadableMessage(), context: context); + } else if (error is HttpieRequestError) { + String errorMessage = await error.toHumanReadableMessage(); + _toastService.error(message: errorMessage, context: context); + } else { + _toastService.error( + message: _localizationService.trans('error__unknown_error'), + context: context); + throw error; + } + } + + Future _createUserPostNotificationsSubscription( + Map notificationSettingsMap) { + return _userService.createPostNotificationsSubscription( + post: widget.post, + commentNotifications: notificationSettingsMap[ + PostNotificationSetting.COMMENT_NOTIFICATIONS], + reactionNotifications: notificationSettingsMap[ + PostNotificationSetting.REACTION_NOTIFICATIONS], + replyNotifications: + notificationSettingsMap[PostNotificationSetting.REPLY_NOTIFICATIONS], + ); + } + + Future _updateUserPostNotificationsSubscription( + Map notificationSettingsMap) { + return _userService.updatePostNotificationsSubscription( + post: widget.post, + commentNotifications: notificationSettingsMap[ + PostNotificationSetting.COMMENT_NOTIFICATIONS], + reactionNotifications: notificationSettingsMap[ + PostNotificationSetting.REACTION_NOTIFICATIONS], + replyNotifications: + notificationSettingsMap[PostNotificationSetting.REPLY_NOTIFICATIONS], + ); + } +} + +class PostNotificationSetting extends NotificationSetting { + String key; + bool value; + bool isDisabled; + String localizedTitle; + String localizedDesc; + + static const COMMENT_NOTIFICATIONS = 'commentNotifications'; + static const REPLY_NOTIFICATIONS = 'replyNotifications'; + static const REACTION_NOTIFICATIONS = 'reactionNotifications'; + List _types = [ + COMMENT_NOTIFICATIONS, + REPLY_NOTIFICATIONS, + REACTION_NOTIFICATIONS + ]; + + PostNotificationSetting( + {this.key, + this.value, + this.isDisabled, + this.localizedTitle, + this.localizedDesc}) { + if (!_types.contains(this.key)) + throw PostNotificationSettingKeyError(this.key); + } +} + +class PostNotificationSettingKeyError extends Error { + final String message; + PostNotificationSettingKeyError(this.message); + String toString() => + "Unsupported key for post notification setting: $message"; +} + +typedef void OnNotificationSettingsSave(); diff --git a/lib/pages/home/modals/manage_notifications/widgets/manage_notifications.dart b/lib/pages/home/modals/manage_notifications/widgets/manage_notifications.dart new file mode 100644 index 000000000..0823c5876 --- /dev/null +++ b/lib/pages/home/modals/manage_notifications/widgets/manage_notifications.dart @@ -0,0 +1,183 @@ +import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/widgets/fields/toggle_field.dart'; +import 'package:Okuna/widgets/icon.dart'; +import 'package:Okuna/widgets/nav_bars/themed_nav_bar.dart'; +import 'package:Okuna/provider.dart'; +import 'package:Okuna/widgets/buttons/button.dart'; +import 'package:Okuna/widgets/theming/primary_color_container.dart'; +import 'package:Okuna/widgets/theming/text.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class OBManageNotifications extends StatefulWidget { + List notificationSettings; + OnWantsToSaveSettings onWantsToSaveSettings; + VoidCallback onWantsToToggleMute; + VoidCallback onDismissModal; + bool isMuted; + + OBManageNotifications( + {Key key, + @required this.onWantsToSaveSettings, + @required this.notificationSettings, + @required this.onWantsToToggleMute, + @required this.onDismissModal, + @required this.isMuted}) + : super(key: key); + + @override + OBManageNotificationsState createState() { + return OBManageNotificationsState(); + } +} + +class OBManageNotificationsState extends State { + LocalizationService _localizationService; + bool _requestInProgress; + bool _isMuted; + + @override + void initState() { + super.initState(); + _requestInProgress = false; + _isMuted = widget.isMuted; + } + + @override + Widget build(BuildContext context) { + var openbookProvider = OpenbookProvider.of(context); + _localizationService = openbookProvider.localizationService; + + return CupertinoPageScaffold( + navigationBar: _buildNavigationBar(), + child: OBPrimaryColorContainer( + child: Column( + children: [ + Expanded(child: _buildNotificationSettings()), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20), + child: Row( + children: [ + Expanded( + child: OBButton( + size: OBButtonSize.large, + type: OBButtonType.highlight, + child: _isMuted + ? Text(_localizationService + .post__unmute_post_notifications_text) + : Text(_localizationService + .post__mute_post_notifications_text), + onPressed: _onWantsToToggleMute, + ), + ), + const SizedBox( + width: 20, + ), + Expanded( + child: OBButton( + size: OBButtonSize.large, + child: Text(_localizationService + .post__save_post_notifications_text), + onPressed: _onWantsToSaveSettings, + isLoading: _requestInProgress, + ), + ) + ], + ), + ) + ], + ))); + } + + void _toggleNotificationSetting(NotificationSetting setting) { + setState(() { + setting.value = !setting.value; + }); + } + + void _toggleLocalStateIsMuted() { + setState(() { + _isMuted = !_isMuted; + }); + } + + Widget _buildNavigationBar() { + return OBThemedNavigationBar( + leading: GestureDetector( + child: const OBIcon(OBIcons.close), + onTap: () { + if (widget.onDismissModal != null) widget.onDismissModal(); + Navigator.pop(context); + }, + ), + title: _localizationService.post__post_notifications_title_text); + } + + Widget _buildNotificationSettings() { + return ListView.builder( + physics: const ClampingScrollPhysics(), + itemCount: widget.notificationSettings.length, + itemBuilder: (BuildContext context, int index) { + NotificationSetting notificationSetting = + widget.notificationSettings[index]; + + return OBToggleField( + key: Key(notificationSetting.key), + value: notificationSetting.value, + isDisabled: notificationSetting.isDisabled, + title: notificationSetting.localizedTitle, + subtitle: OBText(notificationSetting.localizedDesc), + onChanged: (bool newValue) => notificationSetting.value = newValue, + onTap: () { + _toggleNotificationSetting(notificationSetting); + }, // toggle + hasDivider: false, + ); + }); + } + + void _onWantsToSaveSettings() { + _setRequestInProgress(true); + Map _notificationSettingsMap = + _getNotificationSettingsMap(widget.notificationSettings); + widget.onWantsToSaveSettings(_notificationSettingsMap); + _setRequestInProgress(false); + // Navigator.pop(context); + } + + Map _getNotificationSettingsMap( + List notificationSettings) { + Map _map = {}; + notificationSettings.forEach((NotificationSetting setting) { + _map[setting.key] = setting.value; + }); + return _map; + } + + void _onWantsToToggleMute() async { + widget.onWantsToToggleMute(); + _toggleLocalStateIsMuted(); + setState(() { + widget.notificationSettings.forEach((NotificationSetting setting) => + setting.isDisabled = !setting.isDisabled); + }); + } + + void _setRequestInProgress(bool requestInProgress) { + setState(() { + _requestInProgress = requestInProgress; + }); + } +} + +abstract class NotificationSetting { + String key; + bool value; + bool isDisabled; + String localizedTitle; + String localizedDesc; +} + +typedef void OnWantsToSaveSettings( + Map notificationSettingsMap); diff --git a/lib/services/localization.dart b/lib/services/localization.dart index 047084989..2b9b91f27 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -2188,6 +2188,92 @@ class LocalizationService { name: 'post__exclude_community_from_profile_posts_confirmation'); } + String get post__manage_post_notifications { + return Intl.message("Manage post notifications", + name: 'post__manage_post_notifications'); + } + + String get post__post_notifications_title_text { + return Intl.message("Post notifications", + name: 'post__post_notifications_title_text'); + } + + String get post__subscribe_post_notifications { + return Intl.message("Subscribe to post notifications", + name: 'post__subscribe_post_notifications'); + } + + String get post__save_post_notifications_text { + return Intl.message("Save", name: 'post__save_post_notifications_text'); + } + + String get post__mute_post_notifications_text { + return Intl.message("Mute post", + name: 'post__mute_post_notifications_text'); + } + + String get post__unmute_post_notifications_text { + return Intl.message("Unmute post", + name: 'post__unmute_post_notifications_text'); + } + + String get post__manage_notifications_comments_title { + return Intl.message("Comments", + name: 'post__manage_notifications_comments_title'); + } + + String get post__manage_notifications_comments_desc { + return Intl.message("Get notifications when someone comments on this post", + name: 'post__manage_notifications_comments_desc'); + } + + String get post__manage_notifications_comment_reactions_title { + return Intl.message("Comment Reactions", + name: 'post__manage_notifications_comment_reactions_title'); + } + + String get post__manage_notifications_comment_reactions_desc { + return Intl.message( + "Get notifications when someone reacts to your comments", + name: 'post__manage_notifications_comment_reactions_desc'); + } + + String get post__manage_notifications_reactions_title { + return Intl.message("Reactions", + name: 'post__manage_notifications_reactions_title'); + } + + String get post__manage_notifications_reactions_desc { + return Intl.message("Get notifications when someone reacts to your post", + name: 'post__manage_notifications_reactions_desc'); + } + + String get post__manage_notifications_replies_title { + return Intl.message("Replies", + name: 'post__manage_notifications_replies_title'); + } + + String get post__manage_notifications_replies_desc { + return Intl.message( + "Get notifications when someone replies to comments on this post", + name: 'post__manage_notifications_reactions_desc'); + } + + String get post__manage_notifications_successfully_saved { + return Intl.message("Notification settings saved", + name: 'post__manage_notifications_successfully_saved'); + } + + String get post__manage_notifications_successfully_muted { + return Intl.message("Notifications muted", + name: 'post__manage_notifications_successfully_muted'); + } + + String get post__manage_notifications_successfully_unmuted { + return Intl.message("Notifications unmuted", + name: 'post__manage_notifications_successfully_unmuted'); + } + String get post__comments_enabled_message { return Intl.message("Comments enabled for post", name: 'post__comments_enabled_message'); @@ -3459,6 +3545,16 @@ class LocalizationService { name: 'notifications__mute_post_turn_off_post_notifications'); } + String get notifications__enable_comment_post_notifications { + return Intl.message("Enable new comment notifications", + name: 'notifications__enable_comment_post_notifications'); + } + + String get notifications__disable_comment_post_notifications { + return Intl.message("Disable new comment notifications", + name: 'notifications__disable_comment_post_notifications'); + } + String get notifications__mute_post_turn_on_post_comment_notifications { return Intl.message("Turn on post comment notifications", name: 'notifications__mute_post_turn_on_post_comment_notifications'); diff --git a/lib/services/modal_service.dart b/lib/services/modal_service.dart index 535dd3fbc..7b63d68b5 100644 --- a/lib/services/modal_service.dart +++ b/lib/services/modal_service.dart @@ -7,11 +7,14 @@ import 'package:Okuna/models/moderation/moderated_object.dart'; import 'package:Okuna/models/moderation/moderation_category.dart'; import 'package:Okuna/models/post.dart'; import 'package:Okuna/models/post_comment.dart'; +import 'package:Okuna/models/post_notifications_subscription.dart'; import 'package:Okuna/models/post_reaction.dart'; import 'package:Okuna/models/user.dart'; import 'package:Okuna/models/user_invite.dart'; import 'package:Okuna/pages/home/modals/accept_guidelines/accept_guidelines.dart'; import 'package:Okuna/pages/home/modals/invite_to_community.dart'; +import 'package:Okuna/pages/home/modals/manage_notifications/manage_post_notifications.dart'; +import 'package:Okuna/pages/home/modals/manage_notifications/widgets/manage_notifications.dart'; import 'package:Okuna/pages/home/modals/post_comment/post_comment_reply_expanded.dart'; import 'package:Okuna/pages/home/modals/post_comment/post_commenter_expanded.dart'; import 'package:Okuna/pages/home/modals/save_post/create_post.dart'; @@ -310,6 +313,23 @@ class ModalService { })); } + Future openManagePostNotifications( + {Post post, + OnNotificationSettingsSave onNotificationSettingsSave, + @required BuildContext context}) { + return Navigator.of(context, rootNavigator: true) + .push(CupertinoPageRoute( + fullscreenDialog: true, + builder: (BuildContext context) { + return Material( + child: OBManagePostNotificationsModal( + onNotificationSettingsSave: onNotificationSettingsSave, + post: post, + ), + ); + })); + } + Future openCreateUserInvite( {@required BuildContext context}) async { UserInvite createdUserInvite = diff --git a/lib/widgets/fields/toggle_field.dart b/lib/widgets/fields/toggle_field.dart index ce898a447..530b83734 100644 --- a/lib/widgets/fields/toggle_field.dart +++ b/lib/widgets/fields/toggle_field.dart @@ -13,6 +13,7 @@ class OBToggleField extends StatelessWidget { final bool hasDivider; final TextStyle titleStyle; final bool isLoading; + final bool isDisabled; const OBToggleField( {Key key, @@ -24,6 +25,7 @@ class OBToggleField extends StatelessWidget { this.subtitle, this.hasDivider = true, this.titleStyle, + this.isDisabled = false, this.isLoading = false}) : super(key: key); @@ -44,16 +46,20 @@ class OBToggleField extends StatelessWidget { subtitle: subtitle, trailing: CupertinoSwitch( value: value, - onChanged: onChanged, + onChanged: (bool newValue) { + if (!isDisabled) onChanged(newValue); + }, ), - onTap: onTap), + onTap: () { + if (!isDisabled) onTap(); + }), ), hasDivider ? OBDivider() : const SizedBox() ], ), ); - if (isLoading) { + if (isLoading || isDisabled) { tile = Opacity( opacity: 0.5, child: tile, diff --git a/lib/widgets/tiles/actions/manage_post_notifications_tile.dart b/lib/widgets/tiles/actions/manage_post_notifications_tile.dart new file mode 100644 index 000000000..2406014b1 --- /dev/null +++ b/lib/widgets/tiles/actions/manage_post_notifications_tile.dart @@ -0,0 +1,79 @@ +import 'package:Okuna/models/post.dart'; +import 'package:Okuna/models/post_notifications_subscription.dart'; +import 'package:Okuna/pages/home/modals/manage_notifications/manage_post_notifications.dart'; +import 'package:Okuna/provider.dart'; +import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/modal_service.dart'; +import 'package:Okuna/services/navigation_service.dart'; +import 'package:Okuna/widgets/icon.dart'; +import 'package:Okuna/widgets/theming/text.dart'; +import 'package:Okuna/widgets/tiles/loading_tile.dart'; +import 'package:flutter/material.dart'; + +class OBManagePostNotificationsTile extends StatefulWidget { + final Post post; + final OnNotificationSettingsSave onNotificationSettingsSave; + final VoidCallback onOpenManagePostNotificationsModal; + + const OBManagePostNotificationsTile({ + Key key, + @required this.post, + this.onOpenManagePostNotificationsModal, + this.onNotificationSettingsSave, + }) : super(key: key); + + @override + OBManagePostNotificationsTileState createState() { + return OBManagePostNotificationsTileState(); + } +} + +class OBManagePostNotificationsTileState + extends State { + ModalService _modalService; + LocalizationService _localizationService; + bool _requestInProgress; + + @override + void initState() { + super.initState(); + _requestInProgress = false; + } + + @override + Widget build(BuildContext context) { + var openbookProvider = OpenbookProvider.of(context); + _modalService = openbookProvider.modalService; + _localizationService = openbookProvider.localizationService; + + return StreamBuilder( + stream: widget.post.updateSubject, + initialData: widget.post, + builder: (BuildContext context, AsyncSnapshot snapshot) { + var post = snapshot.data; + + bool isReported = post.isReported ?? false; + + return OBLoadingTile( + isLoading: _requestInProgress || isReported, + leading: OBIcon(OBIcons.notifications), + title: OBText(widget.post.postNotificationsSubscription != null + ? _localizationService.post__manage_post_notifications + : _localizationService.post__subscribe_post_notifications), + onTap: _navigateToManagePost, + ); + }, + ); + } + + void _navigateToManagePost() { + widget.onOpenManagePostNotificationsModal(); + _modalService.openManagePostNotifications( + context: context, + post: widget.post, + onNotificationSettingsSave: () { + if (widget.onNotificationSettingsSave != null) + widget.onNotificationSettingsSave(); + }); + } +} From 27e13320d1e614564cbd14a173302274bdaa7613 Mon Sep 17 00:00:00 2001 From: Shantanu Date: Wed, 15 Apr 2020 19:16:52 +0200 Subject: [PATCH 13/39] :globe_with_meridians: localization files --- assets/i18n/en/community.arb | 8 +-- assets/i18n/en/notifications.arb | 10 ++++ assets/i18n/en/post.arb | 95 ++++++++++++++++++++++++++++++++ assets/i18n/en/user.arb | 60 ++++++++++++++++++-- assets/i18n/en/user_search.arb | 10 ++++ 5 files changed, 174 insertions(+), 9 deletions(-) diff --git a/assets/i18n/en/community.arb b/assets/i18n/en/community.arb index dcc71759b..872088946 100644 --- a/assets/i18n/en/community.arb +++ b/assets/i18n/en/community.arb @@ -29,12 +29,12 @@ "type": "text", "placeholders": {} }, - "excluded_community": "excluded community", + "excluded_community": "hidden community", "@excluded_community": { "type": "text", "placeholders": {} }, - "excluded_communities": "excluded communities", + "excluded_communities": "hidden communities", "@excluded_communities": { "type": "text", "placeholders": {} @@ -814,12 +814,12 @@ "maxLength": {} } }, - "top_posts_excluded_communities": "Excluded communities", + "top_posts_excluded_communities": "Hidden communities", "@top_posts_excluded_communities": { "type": "text", "placeholders": {} }, - "top_posts_excluded_communities_desc": "Manage communities excluded from the explore timeline", + "top_posts_excluded_communities_desc": "Manage communities hidden from the explore timeline", "@top_posts_excluded_communities_desc": { "type": "text", "placeholders": {} diff --git a/assets/i18n/en/notifications.arb b/assets/i18n/en/notifications.arb index 9041b8a3e..b49f981d3 100644 --- a/assets/i18n/en/notifications.arb +++ b/assets/i18n/en/notifications.arb @@ -144,6 +144,16 @@ "type": "text", "placeholders": {} }, + "enable_comment_post_notifications": "Enable new comment notifications", + "@enable_comment_post_notifications": { + "type": "text", + "placeholders": {} + }, + "disable_comment_post_notifications": "Disable new comment notifications", + "@disable_comment_post_notifications": { + "type": "text", + "placeholders": {} + }, "mute_post_turn_on_post_comment_notifications": "Turn on post comment notifications", "@mute_post_turn_on_post_comment_notifications": { "type": "text", diff --git a/assets/i18n/en/post.arb b/assets/i18n/en/post.arb index 698d8040f..3c901ed95 100644 --- a/assets/i18n/en/post.arb +++ b/assets/i18n/en/post.arb @@ -508,6 +508,101 @@ "type": "text", "placeholders": {} }, + "exclude_community_from_profile_posts": "Hide this community from my profile", + "@exclude_community_from_profile_posts": { + "type": "text", + "placeholders": {} + }, + "exclude_community_from_profile_posts_success": "Community hidden", + "@exclude_community_from_profile_posts_success": { + "type": "text", + "placeholders": {} + }, + "exclude_community_from_profile_posts_confirmation": "This will hide all posts from this community from your profile.", + "@exclude_community_from_profile_posts_confirmation": { + "type": "text", + "placeholders": {} + }, + "manage_post_notifications": "Manage post notifications", + "@manage_post_notifications": { + "type": "text", + "placeholders": {} + }, + "post_notifications_title_text": "Post notifications", + "@post_notifications_title_text": { + "type": "text", + "placeholders": {} + }, + "subscribe_post_notifications": "Subscribe to post notifications", + "@subscribe_post_notifications": { + "type": "text", + "placeholders": {} + }, + "save_post_notifications_text": "Save", + "@save_post_notifications_text": { + "type": "text", + "placeholders": {} + }, + "mute_post_notifications_text": "Mute post", + "@mute_post_notifications_text": { + "type": "text", + "placeholders": {} + }, + "unmute_post_notifications_text": "Unmute post", + "@unmute_post_notifications_text": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_comments_title": "Comments", + "@manage_notifications_comments_title": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_comments_desc": "Get notifications when someone comments on this post", + "@manage_notifications_comments_desc": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_comment_reactions_title": "Comment Reactions", + "@manage_notifications_comment_reactions_title": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_comment_reactions_desc": "Get notifications when someone reacts to your comments", + "@manage_notifications_comment_reactions_desc": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_reactions_title": "Reactions", + "@manage_notifications_reactions_title": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_reactions_desc": "Get notifications when someone reacts to your post", + "@manage_notifications_reactions_desc": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_replies_title": "Replies", + "@manage_notifications_replies_title": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_successfully_saved": "Notification settings saved", + "@manage_notifications_successfully_saved": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_successfully_muted": "Notifications muted", + "@manage_notifications_successfully_muted": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_successfully_unmuted": "Notifications unmuted", + "@manage_notifications_successfully_unmuted": { + "type": "text", + "placeholders": {} + }, "comments_enabled_message": "Comments enabled for post", "@comments_enabled_message": { "type": "text", diff --git a/assets/i18n/en/user.arb b/assets/i18n/en/user.arb index a2bc55b90..a8fa96600 100644 --- a/assets/i18n/en/user.arb +++ b/assets/i18n/en/user.arb @@ -298,18 +298,68 @@ "type": "text", "placeholders": {} }, - "edit_profile_followers_count": "Followers count", - "@edit_profile_followers_count": { + "manage_profile_followers_count_toggle": "Followers count", + "@manage_profile_followers_count_toggle": { "type": "text", "placeholders": {} }, - "edit_profile_community_posts": "Community posts", + "manage_profile_followers_count_toggle__descr": "Display the number of people that follow you, on your profile.", + "@manage_profile_followers_count_toggle__descr": { + "type": "text", + "placeholders": {} + }, + "manage_profile_community_posts_toggle": "Community posts", + "@manage_profile_community_posts_toggle": { + "type": "text", + "placeholders": {} + }, + "manage_profile_community_posts_toggle__descr": "Display posts you share with public communities, on your profile.", + "@manage_profile_community_posts_toggle__descr": { + "type": "text", + "placeholders": {} + }, + "edit_profile_community_posts": "Communities", "@edit_profile_community_posts": { "type": "text", "placeholders": {} }, - "edit_profile_title": "Edit profile", - "@edit_profile_title": { + "edit_profile_community_posts_descr": "Display in profile", + "@edit_profile_community_posts_descr": { + "type": "text", + "placeholders": {} + }, + "manage": "Manage", + "@manage": { + "type": "text", + "placeholders": {} + }, + "manage_profile_title": "Manage profile", + "@manage_profile_title": { + "type": "text", + "placeholders": {} + }, + "manage_profile_details_title": "Details", + "@manage_profile_details_title": { + "type": "text", + "placeholders": {} + }, + "manage_profile_details_title_desc": "Change your username, name, url, location, avatar or cover photo.", + "@manage_profile_details_title_desc": { + "type": "text", + "placeholders": {} + }, + "profile_posts_excluded_communities": "Hidden communities", + "@profile_posts_excluded_communities": { + "type": "text", + "placeholders": {} + }, + "profile_posts_exclude_communities": "Exclude communities", + "@profile_posts_exclude_communities": { + "type": "text", + "placeholders": {} + }, + "profile_posts_excluded_communities_desc": "See, add and remove hidden communities from your profile.", + "@profile_posts_excluded_communities_desc": { "type": "text", "placeholders": {} }, diff --git a/assets/i18n/en/user_search.arb b/assets/i18n/en/user_search.arb index d6e70548f..b0be6d6c5 100644 --- a/assets/i18n/en/user_search.arb +++ b/assets/i18n/en/user_search.arb @@ -84,5 +84,15 @@ "placeholders": { "searchQuery": {} } + }, + "selection_submit": "Submit", + "@selection_submit": { + "type": "text", + "placeholders": {} + }, + "selection_clear_all": "Clear all", + "@selection_clear_all": { + "type": "text", + "placeholders": {} } } \ No newline at end of file From e8f56a89745cc2295f09fe8813b5d481a0076115 Mon Sep 17 00:00:00 2001 From: Shantanu Date: Thu, 23 Apr 2020 13:57:57 +0200 Subject: [PATCH 14/39] :sparkles: manage comment notifications bottom sheet, services --- lib/models/post_comment.dart | 28 ++- ...st_comment_notifications_subscription.dart | 66 ++++++ .../post_notifications_subscription.dart | 8 +- .../manage_post_comment_notifications.dart | 224 ++++++++++++++++++ .../manage_post_notifications.dart | 62 ++--- .../widgets/manage_notifications.dart | 160 +++++++++++++ .../home/bottom_sheets/post_actions.dart | 17 -- .../post_comment_more_actions.dart | 6 +- .../widgets/manage_notifications.dart | 183 -------------- lib/services/bottom_sheet.dart | 39 ++- lib/services/localization.dart | 43 +++- lib/services/modal_service.dart | 20 -- lib/services/posts_api.dart | 50 +++- lib/services/user.dart | 30 +++ ...anage_post_comment_notifications_tile.dart | 73 ++++++ .../manage_post_notifications_tile.dart | 23 +- 16 files changed, 744 insertions(+), 288 deletions(-) create mode 100644 lib/models/post_comment_notifications_subscription.dart create mode 100644 lib/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart rename lib/pages/home/{modals => bottom_sheets}/manage_notifications/manage_post_notifications.dart (85%) create mode 100644 lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart delete mode 100644 lib/pages/home/modals/manage_notifications/widgets/manage_notifications.dart create mode 100644 lib/widgets/tiles/actions/manage_post_comment_notifications_tile.dart diff --git a/lib/models/post_comment.dart b/lib/models/post_comment.dart index 002d51d31..3e72f5ad0 100644 --- a/lib/models/post_comment.dart +++ b/lib/models/post_comment.dart @@ -2,6 +2,7 @@ import 'package:Okuna/models/hashtag.dart'; import 'package:Okuna/models/hashtags_list.dart'; import 'package:Okuna/models/post.dart'; import 'package:Okuna/models/post_comment_list.dart'; +import 'package:Okuna/models/post_comment_notifications_subscription.dart'; import 'package:Okuna/models/post_comment_reaction.dart'; import 'package:Okuna/models/reactions_emoji_count.dart'; import 'package:Okuna/models/reactions_emoji_count_list.dart'; @@ -27,6 +28,7 @@ class PostComment extends UpdatableModel { PostCommentReaction reaction; HashtagsList hashtagsList; Map hashtagsMap; + PostCommentNotificationsSubscription postCommentNotificationsSubscription; Post post; bool isEdited; @@ -81,7 +83,8 @@ class PostComment extends UpdatableModel { this.replies, this.repliesCount, this.reactionsEmojiCounts, - this.reaction}) { + this.reaction, + this.postCommentNotificationsSubscription}) { _updateHashtagsMap(); } @@ -114,7 +117,9 @@ class PostComment extends UpdatableModel { 'reactions_emoji_counts': reactionsEmojiCounts.counts .map((ReactionsEmojiCount reaction) => reaction.toJson()) ?.toList(), - 'reaction': reaction.toJson() + 'reaction': reaction.toJson(), + 'post_comment_notifications_subscription': + postCommentNotificationsSubscription.toJson() }; } @@ -178,6 +183,12 @@ class PostComment extends UpdatableModel { hashtagsList = factory.parseHashtagsList(json['hashtags']); _updateHashtagsMap(); } + + if (json.containsKey('post_comment_notifications_subscription')) { + postCommentNotificationsSubscription = + factory.parsePostCommentNotificationsSubscription( + json['post_comment_notifications_subscription']); + } } String getRelativeCreated() { @@ -331,7 +342,10 @@ class PostCommentFactory extends UpdatableModelFactory { reaction: parseReaction(json['reaction']), hashtagsList: parseHashtagsList(json['hashtags']), reactionsEmojiCounts: - parseReactionsEmojiCounts(json['reactions_emoji_counts'])); + parseReactionsEmojiCounts(json['reactions_emoji_counts']), + postCommentNotificationsSubscription: + parsePostCommentNotificationsSubscription( + json['post_comment_notifications_subscription'])); } Post parsePost(Map post) { @@ -378,6 +392,14 @@ class PostCommentFactory extends UpdatableModelFactory { if (hashtagsList == null) return null; return HashtagsList.fromJson(hashtagsList); } + + PostCommentNotificationsSubscription + parsePostCommentNotificationsSubscription( + Map postCommentNotificationsSubscriptionData) { + if (postCommentNotificationsSubscriptionData == null) return null; + return PostCommentNotificationsSubscription.fromJSON( + postCommentNotificationsSubscriptionData); + } } enum PostCommentsSortType { asc, dec } diff --git a/lib/models/post_comment_notifications_subscription.dart b/lib/models/post_comment_notifications_subscription.dart new file mode 100644 index 000000000..4c191a8cd --- /dev/null +++ b/lib/models/post_comment_notifications_subscription.dart @@ -0,0 +1,66 @@ +import 'package:Okuna/models/updatable_model.dart'; +import 'package:dcache/dcache.dart'; + +class PostCommentNotificationsSubscription + extends UpdatableModel { + final int id; + final int postCommentId; + bool reactionNotifications; + bool replyNotifications; + + PostCommentNotificationsSubscription( + {this.id, + this.postCommentId, + this.reactionNotifications, + this.replyNotifications}); + + static final factory = PostCommentNotificationsSubscriptionFactory(); + + factory PostCommentNotificationsSubscription.fromJSON( + Map json) { + if (json == null) return null; + return factory.fromJson(json); + } + + Map toJson() { + return { + 'id': id, + 'post_comment': postCommentId, + 'reaction_notifications': reactionNotifications, + 'reply_notifications': replyNotifications, + }; + } + + Map getSettingsObject() { + return { + 'replyNotifications': this.replyNotifications, + 'reactionNotifications': this.reactionNotifications + }; + } + + @override + void updateFromJson(Map json) { + if (json.containsKey('reaction_notifications')) { + reactionNotifications = json['reaction_notifications']; + } + if (json.containsKey('reply_notifications')) { + replyNotifications = json['reply_notifications']; + } + } +} + +class PostCommentNotificationsSubscriptionFactory + extends UpdatableModelFactory { + @override + SimpleCache cache = + SimpleCache(storage: UpdatableModelSimpleStorage(size: 20)); + + @override + PostCommentNotificationsSubscription makeFromJson(Map json) { + return PostCommentNotificationsSubscription( + id: json['id'], + postCommentId: json['post_comment'], + reactionNotifications: json['reaction_notifications'], + replyNotifications: json['reply_notifications']); + } +} diff --git a/lib/models/post_notifications_subscription.dart b/lib/models/post_notifications_subscription.dart index e39ba1ed2..2263f46f6 100644 --- a/lib/models/post_notifications_subscription.dart +++ b/lib/models/post_notifications_subscription.dart @@ -4,14 +4,14 @@ import 'package:dcache/dcache.dart'; class PostNotificationsSubscription extends UpdatableModel { final int id; - final String postUuid; + final int postId; bool commentNotifications; bool reactionNotifications; bool replyNotifications; PostNotificationsSubscription( {this.id, - this.postUuid, + this.postId, this.commentNotifications, this.reactionNotifications, this.replyNotifications}); @@ -26,7 +26,7 @@ class PostNotificationsSubscription Map toJson() { return { 'id': id, - 'post_uuid': postUuid, + 'post': postId, 'comment_notifications': commentNotifications, 'reaction_notifications': reactionNotifications, 'reply_notifications': replyNotifications, @@ -65,7 +65,7 @@ class PostNotificationsSubscriptionFactory PostNotificationsSubscription makeFromJson(Map json) { return PostNotificationsSubscription( id: json['id'], - postUuid: json['post_uuid'], + postId: json['post'], commentNotifications: json['comment_notifications'], reactionNotifications: json['reaction_notifications'], replyNotifications: json['reply_notifications']); diff --git a/lib/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart b/lib/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart new file mode 100644 index 000000000..51baa30dc --- /dev/null +++ b/lib/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart @@ -0,0 +1,224 @@ +import 'package:Okuna/models/post.dart'; +import 'package:Okuna/models/post_comment.dart'; +import 'package:Okuna/models/post_comment_notifications_subscription.dart'; +import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart'; +import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/provider.dart'; +import 'package:Okuna/services/toast.dart'; +import 'package:Okuna/services/user.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:async/async.dart'; + +import '../rounded_bottom_sheet.dart'; + +class OBManagePostCommentNotificationsBottomSheet extends StatefulWidget { + final Post post; + final PostComment postComment; + final OnNotificationSettingsSave onNotificationSettingsSave; + + OBManagePostCommentNotificationsBottomSheet( + {Key key, + @required this.post, + @required this.postComment, + this.onNotificationSettingsSave}) + : super(key: key); + + @override + OBManagePostCommentNotificationsBottomSheetState createState() { + return OBManagePostCommentNotificationsBottomSheetState(); + } +} + +class OBManagePostCommentNotificationsBottomSheetState + extends State { + UserService _userService; + LocalizationService _localizationService; + ToastService _toastService; + PostCommentNotificationsSubscription postCommentNotificationsSubscription; + CancelableOperation _saveOperation; + CancelableOperation _muteOperation; + + @override + void initState() { + super.initState(); + postCommentNotificationsSubscription = + widget.postComment.postCommentNotificationsSubscription; + } + + @override + Widget build(BuildContext context) { + var openbookProvider = OpenbookProvider.of(context); + _userService = openbookProvider.userService; + _localizationService = openbookProvider.localizationService; + _toastService = openbookProvider.toastService; + + return OBRoundedBottomSheet( + child: OBManageNotifications( + notificationSettings: _getNotificationSettingsList(), + onWantsToSaveSettings: _onWantsToSaveSettings, + mutePostLabelText: + _localizationService.post__mute_post_comment_notifications_text, + unmutePostLabelText: _localizationService + .post__unmute_post_comment_notifications_text, + isMuted: widget.postComment.isMuted, + onWantsToToggleMute: _onWantsToToggleMute)); + } + + @override + void dispose() { + super.dispose(); + if (_saveOperation != null) _saveOperation.cancel(); + if (_muteOperation != null) _muteOperation.cancel(); + } + + List _getNotificationSettingsList() { + List _notificationSettings = [ + PostCommentNotificationSetting( + key: PostCommentNotificationSetting.REPLY_NOTIFICATIONS, + value: + postCommentNotificationsSubscription?.replyNotifications ?? false, + isDisabled: widget.postComment.isMuted, + localizedTitle: + _localizationService.post__manage_notifications_replies_title, + localizedDesc: _localizationService + .post__manage_notifications_replies_post_comment_desc), + ]; + // add post reaction setting if user is creator + if (widget.postComment.commenter == _userService.getLoggedInUser()) { + _notificationSettings.insert( + 0, + PostCommentNotificationSetting( + key: PostCommentNotificationSetting.REACTION_NOTIFICATIONS, + value: + postCommentNotificationsSubscription?.reactionNotifications ?? + false, + isDisabled: widget.postComment.isMuted, + localizedTitle: _localizationService + .post__manage_notifications_reactions_title, + localizedDesc: _localizationService + .post__manage_notifications_reactions_post_comment_desc)); + } + return _notificationSettings; + } + + void _onWantsToToggleMute() async { + try { + if (widget.postComment.isMuted) { + _muteOperation = CancelableOperation.fromFuture( + _userService.unmutePostComment( + post: widget.post, postComment: widget.postComment)); + await _muteOperation.value; + _toastService.success( + message: _localizationService + .post__manage_notifications_successfully_unmuted, + context: context); + } else { + _muteOperation = CancelableOperation.fromFuture( + _userService.mutePostComment( + post: widget.post, postComment: widget.postComment)); + await _muteOperation.value; + _toastService.success( + message: _localizationService + .post__manage_notifications_successfully_muted, + context: context); + } + } catch (error) { + _onError(error); + } + } + + void _onWantsToSaveSettings( + Map notificationSettingsMap) async { + try { + if (widget.postComment.postCommentNotificationsSubscription == null) { + _saveOperation = CancelableOperation.fromFuture( + createUserPostCommentNotificationsSubscription( + notificationSettingsMap)); + await _saveOperation.value; + } else { + _saveOperation = CancelableOperation.fromFuture( + _updateUserPostCommentNotificationsSubscription( + notificationSettingsMap)); + await _saveOperation.value; + } + } catch (error) { + _onError(error); + } + if (widget.onNotificationSettingsSave != null) + widget.onNotificationSettingsSave(); + } + + void _onError(error) async { + if (error is HttpieConnectionRefusedError) { + _toastService.error( + message: error.toHumanReadableMessage(), context: context); + } else if (error is HttpieRequestError) { + String errorMessage = await error.toHumanReadableMessage(); + _toastService.error(message: errorMessage, context: context); + } else { + _toastService.error( + message: _localizationService.trans('error__unknown_error'), + context: context); + throw error; + } + } + + Future createUserPostCommentNotificationsSubscription( + Map notificationSettingsMap) { + return _userService.createPostCommentNotificationsSubscription( + post: widget.post, + postComment: widget.postComment, + reactionNotifications: notificationSettingsMap[ + PostCommentNotificationSetting.REACTION_NOTIFICATIONS], + replyNotifications: notificationSettingsMap[ + PostCommentNotificationSetting.REPLY_NOTIFICATIONS], + ); + } + + Future _updateUserPostCommentNotificationsSubscription( + Map notificationSettingsMap) { + return _userService.updatePostCommentNotificationsSubscription( + post: widget.post, + postComment: widget.postComment, + reactionNotifications: notificationSettingsMap[ + PostCommentNotificationSetting.REACTION_NOTIFICATIONS], + replyNotifications: notificationSettingsMap[ + PostCommentNotificationSetting.REPLY_NOTIFICATIONS], + ); + } +} + +class PostCommentNotificationSetting extends NotificationSetting { + String key; + bool value; + bool isDisabled; + String localizedTitle; + String localizedDesc; + + static const REPLY_NOTIFICATIONS = 'replyNotifications'; + static const REACTION_NOTIFICATIONS = 'reactionNotifications'; + List _notificationTypes = [ + REPLY_NOTIFICATIONS, + REACTION_NOTIFICATIONS + ]; + + PostCommentNotificationSetting( + {this.key, + this.value, + this.isDisabled, + this.localizedTitle, + this.localizedDesc}) { + if (!_notificationTypes.contains(this.key)) + throw PostCommentNotificationSettingKeyError(this.key); + } +} + +class PostCommentNotificationSettingKeyError extends Error { + final String message; + PostCommentNotificationSettingKeyError(this.message); + String toString() => + "Unsupported key for post comment notification setting: $message"; +} + +typedef void OnNotificationSettingsSave(); diff --git a/lib/pages/home/modals/manage_notifications/manage_post_notifications.dart b/lib/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart similarity index 85% rename from lib/pages/home/modals/manage_notifications/manage_post_notifications.dart rename to lib/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart index 0de4ad66a..4a7017d6c 100644 --- a/lib/pages/home/modals/manage_notifications/manage_post_notifications.dart +++ b/lib/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart @@ -1,6 +1,6 @@ import 'package:Okuna/models/post.dart'; import 'package:Okuna/models/post_notifications_subscription.dart'; -import 'package:Okuna/pages/home/modals/manage_notifications/widgets/manage_notifications.dart'; +import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/toast.dart'; @@ -9,22 +9,24 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:async/async.dart'; -class OBManagePostNotificationsModal extends StatefulWidget { +import '../rounded_bottom_sheet.dart'; + +class OBManagePostNotificationsBottomSheet extends StatefulWidget { final Post post; final OnNotificationSettingsSave onNotificationSettingsSave; - OBManagePostNotificationsModal( + OBManagePostNotificationsBottomSheet( {Key key, this.onNotificationSettingsSave, this.post}) : super(key: key); @override - OBManagePostNotificationsModalState createState() { - return OBManagePostNotificationsModalState(); + OBManagePostNotificationsBottomSheetState createState() { + return OBManagePostNotificationsBottomSheetState(); } } -class OBManagePostNotificationsModalState - extends State { +class OBManagePostNotificationsBottomSheetState + extends State { UserService _userService; LocalizationService _localizationService; ToastService _toastService; @@ -45,20 +47,23 @@ class OBManagePostNotificationsModalState _localizationService = openbookProvider.localizationService; _toastService = openbookProvider.toastService; - return OBManageNotifications( - notificationSettings: _getNotificationSettingsList(), - onWantsToSaveSettings: _onWantsToSaveSettings, - isMuted: widget.post.isMuted, - onWantsToToggleMute: _onWantsToToggleMute, - onDismissModal: _onDismiss); + return OBRoundedBottomSheet( + child: OBManageNotifications( + notificationSettings: _getNotificationSettingsList(), + onWantsToSaveSettings: _onWantsToSaveSettings, + isMuted: widget.post.isMuted, + mutePostLabelText: + _localizationService.post__mute_post_notifications_text, + unmutePostLabelText: + _localizationService.post__unmute_post_notifications_text, + onWantsToToggleMute: _onWantsToToggleMute)); } - void _onDismiss() { + @override + void dispose() { + super.dispose(); if (_saveOperation != null) _saveOperation.cancel(); if (_muteOperation != null) _muteOperation.cancel(); - if (widget.onNotificationSettingsSave != null) { - widget.onNotificationSettingsSave(); - } } List _getNotificationSettingsList() { @@ -99,21 +104,21 @@ class OBManagePostNotificationsModalState void _onWantsToToggleMute() async { try { - if (!widget.post.isMuted) { - _muteOperation = - CancelableOperation.fromFuture(_userService.mutePost(widget.post)); + if (widget.post.isMuted) { + _muteOperation = CancelableOperation.fromFuture( + _userService.unmutePost(widget.post)); await _muteOperation.value; _toastService.success( message: _localizationService - .post__manage_notifications_successfully_muted, + .post__manage_notifications_successfully_unmuted, context: context); } else { - _muteOperation = CancelableOperation.fromFuture( - _userService.unmutePost(widget.post)); + _muteOperation = + CancelableOperation.fromFuture(_userService.mutePost(widget.post)); await _muteOperation.value; _toastService.success( message: _localizationService - .post__manage_notifications_successfully_unmuted, + .post__manage_notifications_successfully_muted, context: context); } } catch (error) { @@ -133,16 +138,11 @@ class OBManagePostNotificationsModalState _updateUserPostNotificationsSubscription(notificationSettingsMap)); await _saveOperation.value; } - _toastService.success( - message: _localizationService - .post__manage_notifications_successfully_saved, - context: context); } catch (error) { _onError(error); } if (widget.onNotificationSettingsSave != null) widget.onNotificationSettingsSave(); - Navigator.pop(context); } void _onError(error) async { @@ -197,7 +197,7 @@ class PostNotificationSetting extends NotificationSetting { static const COMMENT_NOTIFICATIONS = 'commentNotifications'; static const REPLY_NOTIFICATIONS = 'replyNotifications'; static const REACTION_NOTIFICATIONS = 'reactionNotifications'; - List _types = [ + List _notificationTypes = [ COMMENT_NOTIFICATIONS, REPLY_NOTIFICATIONS, REACTION_NOTIFICATIONS @@ -209,7 +209,7 @@ class PostNotificationSetting extends NotificationSetting { this.isDisabled, this.localizedTitle, this.localizedDesc}) { - if (!_types.contains(this.key)) + if (!_notificationTypes.contains(this.key)) throw PostNotificationSettingKeyError(this.key); } } diff --git a/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart b/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart new file mode 100644 index 000000000..e67febc94 --- /dev/null +++ b/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart @@ -0,0 +1,160 @@ +import 'package:Okuna/widgets/fields/toggle_field.dart'; +import 'package:Okuna/widgets/buttons/button.dart'; +import 'package:Okuna/widgets/theming/text.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class OBManageNotifications extends StatefulWidget { + List notificationSettings; + OnWantsToSaveSettings onWantsToSaveSettings; + String mutePostLabelText; + String unmutePostLabelText; + VoidCallback onWantsToToggleMute; + bool isMuted; + + OBManageNotifications( + {Key key, + @required this.onWantsToSaveSettings, + @required this.notificationSettings, + @required this.mutePostLabelText, + @required this.unmutePostLabelText, + @required this.onWantsToToggleMute, + @required this.isMuted}) + : super(key: key); + + @override + OBManageNotificationsState createState() { + return OBManageNotificationsState(); + } +} + +class OBManageNotificationsState extends State { + bool _requestInProgress; + bool _isMuted; + + @override + void initState() { + super.initState(); + _requestInProgress = false; + _isMuted = widget.isMuted; + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric(vertical: 15.0), + child: Opacity( + opacity: _requestInProgress ? 0.5 : 1, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildNotificationSettings(), + ))); + } + + void _toggleNotificationSetting(NotificationSetting setting) { + setState(() { + setting.value = !setting.value; + }); + } + + void _toggleLocalStateIsMuted() { + setState(() { + _isMuted = !_isMuted; + }); + } + + List _buildNotificationSettings() { + List _settings = []; + + widget.notificationSettings + .forEach((NotificationSetting notificationSetting) => { + _settings.add(OBToggleField( + key: Key(notificationSetting.key), + value: notificationSetting.value, + isDisabled: notificationSetting.isDisabled, + title: notificationSetting.localizedTitle, + subtitle: OBText(notificationSetting.localizedDesc), + onChanged: (bool newValue) => + notificationSetting.value = newValue, + onTap: () { + _toggleNotificationSetting(notificationSetting); + _onWantsToSaveSettings(); + }, // toggle + hasDivider: false, + )) + }); + + _settings.add(_buildMuteButtonRow()); + + return _settings; + } + + Widget _buildMuteButtonRow() { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20), + child: Row( + children: [ + Expanded( + child: OBButton( + size: OBButtonSize.large, + type: OBButtonType.highlight, + child: _isMuted + ? Text(widget.unmutePostLabelText) + : Text(widget.mutePostLabelText), + onPressed: _onWantsToToggleMute, + ), + ), + ], + ), + ); + } + + void _onWantsToSaveSettings() { + _setRequestInProgress(true); + Map _notificationSettingsMap = + _getNotificationSettingsMap(widget.notificationSettings); + widget.onWantsToSaveSettings(_notificationSettingsMap); + _setRequestInProgress(false); + } + + Map _getNotificationSettingsMap( + List notificationSettings) { + Map _map = {}; + notificationSettings.forEach((NotificationSetting setting) { + _map[setting.key] = setting.value; + }); + return _map; + } + + void _onWantsToToggleMute() async { + try { + widget.onWantsToToggleMute(); + _toggleLocalStateIsMuted(); + } catch (error) { + print('Manage Notifications Error while toggling mute'); + } + + setState(() { + widget.notificationSettings.forEach((NotificationSetting setting) => + setting.isDisabled = !setting.isDisabled); + }); + } + + void _setRequestInProgress(bool requestInProgress) { + setState(() { + _requestInProgress = requestInProgress; + }); + } +} + +abstract class NotificationSetting { + String key; + bool value; + bool isDisabled; + String localizedTitle; + String localizedDesc; +} + +typedef void OnWantsToSaveSettings( + Map notificationSettingsMap); diff --git a/lib/pages/home/bottom_sheets/post_actions.dart b/lib/pages/home/bottom_sheets/post_actions.dart index d6a7fdeff..e9bcdcf73 100644 --- a/lib/pages/home/bottom_sheets/post_actions.dart +++ b/lib/pages/home/bottom_sheets/post_actions.dart @@ -13,11 +13,9 @@ import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/post/post.dart'; import 'package:Okuna/widgets/theming/text.dart'; import 'package:Okuna/widgets/tiles/actions/close_post_tile.dart'; -import 'package:Okuna/widgets/tiles/actions/disable_comments_post_tile.dart'; import 'package:Okuna/widgets/tiles/actions/exclude_community_from_profile_posts_tile.dart'; import 'package:Okuna/widgets/tiles/actions/exclude_community_from_top_posts_tile.dart'; import 'package:Okuna/widgets/tiles/actions/manage_post_notifications_tile.dart'; -import 'package:Okuna/widgets/tiles/actions/mute_post_tile.dart'; import 'package:Okuna/widgets/tiles/actions/report_post_tile.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -97,26 +95,11 @@ class OBPostActionsBottomSheetState extends State { widget.onPostCommunityExcludedFromProfilePosts)); } -// postActions.add(OBMutePostTile( -// post: post, -// onMutedPost: _dismiss, -// onUnmutedPost: _dismiss, -// )); - postActions.add(OBManagePostNotificationsTile( post: post, - onOpenManagePostNotificationsModal: _dismiss, onNotificationSettingsSave: _dismiss, )); -// if (loggedInUser.canDisableOrEnableCommentsForPost(post)) { -// postActions.add(OBDisableCommentsPostTile( -// post: post, -// onDisableComments: _dismiss, -// onEnableComments: _dismiss, -// )); -// } - if (loggedInUser.canCloseOrOpenPost(post)) { postActions.add(OBClosePostTile( post: post, diff --git a/lib/pages/home/bottom_sheets/post_comment_more_actions.dart b/lib/pages/home/bottom_sheets/post_comment_more_actions.dart index 2e531956d..b0d05a0f0 100644 --- a/lib/pages/home/bottom_sheets/post_comment_more_actions.dart +++ b/lib/pages/home/bottom_sheets/post_comment_more_actions.dart @@ -11,6 +11,7 @@ import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/user.dart'; import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/theming/text.dart'; +import 'package:Okuna/widgets/tiles/actions/manage_post_comment_notifications_tile.dart'; import 'package:Okuna/widgets/tiles/actions/mute_post_comment_tile.dart'; import 'package:async/async.dart'; import 'package:flutter/material.dart'; @@ -81,9 +82,10 @@ class OBPostCommentMoreActionsBottomSheetState List _buildActionTiles() { List actionTiles = [ - OBMutePostCommentTile( - postComment: widget.postComment, + OBManagePostCommentNotificationsTile( post: widget.post, + postComment: widget.postComment, + onNotificationSettingsSave: _dismissMoreActions, ) ]; User loggedInUser = _userService.getLoggedInUser(); diff --git a/lib/pages/home/modals/manage_notifications/widgets/manage_notifications.dart b/lib/pages/home/modals/manage_notifications/widgets/manage_notifications.dart deleted file mode 100644 index 0823c5876..000000000 --- a/lib/pages/home/modals/manage_notifications/widgets/manage_notifications.dart +++ /dev/null @@ -1,183 +0,0 @@ -import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/widgets/fields/toggle_field.dart'; -import 'package:Okuna/widgets/icon.dart'; -import 'package:Okuna/widgets/nav_bars/themed_nav_bar.dart'; -import 'package:Okuna/provider.dart'; -import 'package:Okuna/widgets/buttons/button.dart'; -import 'package:Okuna/widgets/theming/primary_color_container.dart'; -import 'package:Okuna/widgets/theming/text.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; - -class OBManageNotifications extends StatefulWidget { - List notificationSettings; - OnWantsToSaveSettings onWantsToSaveSettings; - VoidCallback onWantsToToggleMute; - VoidCallback onDismissModal; - bool isMuted; - - OBManageNotifications( - {Key key, - @required this.onWantsToSaveSettings, - @required this.notificationSettings, - @required this.onWantsToToggleMute, - @required this.onDismissModal, - @required this.isMuted}) - : super(key: key); - - @override - OBManageNotificationsState createState() { - return OBManageNotificationsState(); - } -} - -class OBManageNotificationsState extends State { - LocalizationService _localizationService; - bool _requestInProgress; - bool _isMuted; - - @override - void initState() { - super.initState(); - _requestInProgress = false; - _isMuted = widget.isMuted; - } - - @override - Widget build(BuildContext context) { - var openbookProvider = OpenbookProvider.of(context); - _localizationService = openbookProvider.localizationService; - - return CupertinoPageScaffold( - navigationBar: _buildNavigationBar(), - child: OBPrimaryColorContainer( - child: Column( - children: [ - Expanded(child: _buildNotificationSettings()), - Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20), - child: Row( - children: [ - Expanded( - child: OBButton( - size: OBButtonSize.large, - type: OBButtonType.highlight, - child: _isMuted - ? Text(_localizationService - .post__unmute_post_notifications_text) - : Text(_localizationService - .post__mute_post_notifications_text), - onPressed: _onWantsToToggleMute, - ), - ), - const SizedBox( - width: 20, - ), - Expanded( - child: OBButton( - size: OBButtonSize.large, - child: Text(_localizationService - .post__save_post_notifications_text), - onPressed: _onWantsToSaveSettings, - isLoading: _requestInProgress, - ), - ) - ], - ), - ) - ], - ))); - } - - void _toggleNotificationSetting(NotificationSetting setting) { - setState(() { - setting.value = !setting.value; - }); - } - - void _toggleLocalStateIsMuted() { - setState(() { - _isMuted = !_isMuted; - }); - } - - Widget _buildNavigationBar() { - return OBThemedNavigationBar( - leading: GestureDetector( - child: const OBIcon(OBIcons.close), - onTap: () { - if (widget.onDismissModal != null) widget.onDismissModal(); - Navigator.pop(context); - }, - ), - title: _localizationService.post__post_notifications_title_text); - } - - Widget _buildNotificationSettings() { - return ListView.builder( - physics: const ClampingScrollPhysics(), - itemCount: widget.notificationSettings.length, - itemBuilder: (BuildContext context, int index) { - NotificationSetting notificationSetting = - widget.notificationSettings[index]; - - return OBToggleField( - key: Key(notificationSetting.key), - value: notificationSetting.value, - isDisabled: notificationSetting.isDisabled, - title: notificationSetting.localizedTitle, - subtitle: OBText(notificationSetting.localizedDesc), - onChanged: (bool newValue) => notificationSetting.value = newValue, - onTap: () { - _toggleNotificationSetting(notificationSetting); - }, // toggle - hasDivider: false, - ); - }); - } - - void _onWantsToSaveSettings() { - _setRequestInProgress(true); - Map _notificationSettingsMap = - _getNotificationSettingsMap(widget.notificationSettings); - widget.onWantsToSaveSettings(_notificationSettingsMap); - _setRequestInProgress(false); - // Navigator.pop(context); - } - - Map _getNotificationSettingsMap( - List notificationSettings) { - Map _map = {}; - notificationSettings.forEach((NotificationSetting setting) { - _map[setting.key] = setting.value; - }); - return _map; - } - - void _onWantsToToggleMute() async { - widget.onWantsToToggleMute(); - _toggleLocalStateIsMuted(); - setState(() { - widget.notificationSettings.forEach((NotificationSetting setting) => - setting.isDisabled = !setting.isDisabled); - }); - } - - void _setRequestInProgress(bool requestInProgress) { - setState(() { - _requestInProgress = requestInProgress; - }); - } -} - -abstract class NotificationSetting { - String key; - bool value; - bool isDisabled; - String localizedTitle; - String localizedDesc; -} - -typedef void OnWantsToSaveSettings( - Map notificationSettingsMap); diff --git a/lib/services/bottom_sheet.dart b/lib/services/bottom_sheet.dart index 927feea1f..84ac3e0df 100644 --- a/lib/services/bottom_sheet.dart +++ b/lib/services/bottom_sheet.dart @@ -17,6 +17,8 @@ import 'package:Okuna/pages/home/bottom_sheets/hashtag_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/hashtags_display_setting_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/image_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/link_previews_setting_picker.dart'; +import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart'; +import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart'; import 'package:Okuna/pages/home/bottom_sheets/post_comment_more_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/follows_lists_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/post_actions.dart'; @@ -174,13 +176,48 @@ class BottomSheetService { displayContext: displayContext, onCommunityExcluded: onCommunityExcluded, onUndoCommunityExcluded: onUndoCommunityExcluded, - onPostCommunityExcludedFromProfilePosts: onPostCommunityExcludedFromProfilePosts, + onPostCommunityExcludedFromProfilePosts: + onPostCommunityExcludedFromProfilePosts, onPostDeleted: onPostDeleted, onPostReported: onPostReported, ); }); } + Future showManagePostNotifications( + {@required BuildContext context, + @required Post post, + VoidCallback onNotificationSettingsSave}) { + return _showModalBottomSheetApp( + context: context, + builder: (BuildContext context) { + return Material( + child: OBManagePostNotificationsBottomSheet( + post: post, + onNotificationSettingsSave: onNotificationSettingsSave, + ), + ); + }); + } + + Future showManagePostCommentNotifications( + {@required BuildContext context, + @required Post post, + @required PostComment postComment, + VoidCallback onNotificationSettingsSave}) { + return _showModalBottomSheetApp( + context: context, + builder: (BuildContext context) { + return Material( + child: OBManagePostCommentNotificationsBottomSheet( + post: post, + postComment: postComment, + onNotificationSettingsSave: onNotificationSettingsSave, + ), + ); + }); + } + Future showHashtagActions( {@required BuildContext context, @required Hashtag hashtag, diff --git a/lib/services/localization.dart b/lib/services/localization.dart index 2b9b91f27..a63684597 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -2193,9 +2193,9 @@ class LocalizationService { name: 'post__manage_post_notifications'); } - String get post__post_notifications_title_text { - return Intl.message("Post notifications", - name: 'post__post_notifications_title_text'); + String get post__manage_post_comment_notifications { + return Intl.message("Manage comment notifications", + name: 'post__manage_post_comment_notifications'); } String get post__subscribe_post_notifications { @@ -2203,8 +2203,9 @@ class LocalizationService { name: 'post__subscribe_post_notifications'); } - String get post__save_post_notifications_text { - return Intl.message("Save", name: 'post__save_post_notifications_text'); + String get post__subscribe_post_comment_notifications { + return Intl.message("Subscribe to comment notifications", + name: 'post__subscribe_post_comment_notifications'); } String get post__mute_post_notifications_text { @@ -2217,6 +2218,16 @@ class LocalizationService { name: 'post__unmute_post_notifications_text'); } + String get post__mute_post_comment_notifications_text { + return Intl.message("Mute comment", + name: 'post__mute_post_comment_notifications_text'); + } + + String get post__unmute_post_comment_notifications_text { + return Intl.message("Unmute comment", + name: 'post__unmute_post_comment_notifications_text'); + } + String get post__manage_notifications_comments_title { return Intl.message("Comments", name: 'post__manage_notifications_comments_title'); @@ -2248,6 +2259,11 @@ class LocalizationService { name: 'post__manage_notifications_reactions_desc'); } + String get post__manage_notifications_reactions_post_comment_desc { + return Intl.message("Get notifications when someone reacts to your comment", + name: 'post__manage_notifications_reactions_post_comment_desc'); + } + String get post__manage_notifications_replies_title { return Intl.message("Replies", name: 'post__manage_notifications_replies_title'); @@ -2259,6 +2275,12 @@ class LocalizationService { name: 'post__manage_notifications_reactions_desc'); } + String get post__manage_notifications_replies_post_comment_desc { + return Intl.message( + "Get notifications when someone replies to this comment", + name: 'post__manage_notifications_replies_post_comment_desc'); + } + String get post__manage_notifications_successfully_saved { return Intl.message("Notification settings saved", name: 'post__manage_notifications_successfully_saved'); @@ -3502,6 +3524,17 @@ class LocalizationService { name: 'notifications__post_reaction_desc'); } + String get notifications__post_notifications_title { + return Intl.message("Post notifications", + name: 'notifications__post_notifications_title'); + } + + String get notifications__post_notifications_desc { + return Intl.message( + "Be notified of all post related notifications - comments, replies, reactions and user mentions", + name: 'notifications__post_notifications_desc'); + } + String get notifications__community_invite_title { return Intl.message("Community invite", name: 'notifications__community_invite_title'); diff --git a/lib/services/modal_service.dart b/lib/services/modal_service.dart index 7b63d68b5..535dd3fbc 100644 --- a/lib/services/modal_service.dart +++ b/lib/services/modal_service.dart @@ -7,14 +7,11 @@ import 'package:Okuna/models/moderation/moderated_object.dart'; import 'package:Okuna/models/moderation/moderation_category.dart'; import 'package:Okuna/models/post.dart'; import 'package:Okuna/models/post_comment.dart'; -import 'package:Okuna/models/post_notifications_subscription.dart'; import 'package:Okuna/models/post_reaction.dart'; import 'package:Okuna/models/user.dart'; import 'package:Okuna/models/user_invite.dart'; import 'package:Okuna/pages/home/modals/accept_guidelines/accept_guidelines.dart'; import 'package:Okuna/pages/home/modals/invite_to_community.dart'; -import 'package:Okuna/pages/home/modals/manage_notifications/manage_post_notifications.dart'; -import 'package:Okuna/pages/home/modals/manage_notifications/widgets/manage_notifications.dart'; import 'package:Okuna/pages/home/modals/post_comment/post_comment_reply_expanded.dart'; import 'package:Okuna/pages/home/modals/post_comment/post_commenter_expanded.dart'; import 'package:Okuna/pages/home/modals/save_post/create_post.dart'; @@ -313,23 +310,6 @@ class ModalService { })); } - Future openManagePostNotifications( - {Post post, - OnNotificationSettingsSave onNotificationSettingsSave, - @required BuildContext context}) { - return Navigator.of(context, rootNavigator: true) - .push(CupertinoPageRoute( - fullscreenDialog: true, - builder: (BuildContext context) { - return Material( - child: OBManagePostNotificationsModal( - onNotificationSettingsSave: onNotificationSettingsSave, - post: post, - ), - ); - })); - } - Future openCreateUserInvite( {@required BuildContext context}) async { UserInvite createdUserInvite = diff --git a/lib/services/posts_api.dart b/lib/services/posts_api.dart index a56accbb3..260d9aca4 100644 --- a/lib/services/posts_api.dart +++ b/lib/services/posts_api.dart @@ -44,6 +44,8 @@ class PostsApiService { static const UNMUTE_POST_PATH = 'api/posts/{postUuid}/notifications/unmute/'; static const POST_NOTIFICATIONS_SUBSCRIPTION_PATH = 'api/posts/{postUuid}/notifications/subscribe/'; + static const POST_COMMENT_NOTIFICATIONS_SUBSCRIPTION_PATH = + 'api/posts/{postUuid}/comments/{postCommentId}/notifications/subscribe/'; static const REPORT_POST_PATH = 'api/posts/{postUuid}/report/'; static const PREVIEW_POST_DATA_PATH = 'api/posts/{postUuid}/link-preview/'; static const TRANSLATE_POST_PATH = 'api/posts/{postUuid}/translate/'; @@ -439,6 +441,21 @@ class PostsApiService { } Future createPostNotificationsSubscription( + {@required String postUuid, + bool commentNotifications = false, + bool reactionNotifications = false, + bool replyNotifications = false}) { + Map body = {}; + + body['comment_notifications'] = commentNotifications ?? false; + body['reaction_notifications'] = reactionNotifications ?? false; + body['reply_notifications'] = replyNotifications ?? false; + String path = _makePostNotificationsSubscriptionPath(postUuid); + return _httpService.putJSON(_makeApiUrl(path), + body: body, appendAuthorizationToken: true); + } + + Future updatePostNotificationsSubscription( {@required String postUuid, bool commentNotifications, bool reactionNotifications, @@ -454,26 +471,38 @@ class PostsApiService { body['reply_notifications'] = replyNotifications; } String path = _makePostNotificationsSubscriptionPath(postUuid); + return _httpService.patchJSON(_makeApiUrl(path), + body: body, appendAuthorizationToken: true); + } + + Future createPostCommentNotificationsSubscription( + {@required String postUuid, + @required int postCommentId, + bool reactionNotifications = false, + bool replyNotifications = false}) { + Map body = {}; + body['reaction_notifications'] = reactionNotifications ?? false; + body['reply_notifications'] = replyNotifications ?? false; + String path = _makePostCommentNotificationsSubscriptionPath( + postUuid: postUuid, postCommentId: postCommentId); return _httpService.putJSON(_makeApiUrl(path), body: body, appendAuthorizationToken: true); } - Future updatePostNotificationsSubscription( + Future updatePostCommentNotificationsSubscription( {@required String postUuid, - bool commentNotifications, + @required int postCommentId, bool reactionNotifications, bool replyNotifications}) { Map body = {}; - if (commentNotifications != null) { - body['comment_notifications'] = commentNotifications; - } if (reactionNotifications != null) { body['reaction_notifications'] = reactionNotifications; } if (replyNotifications != null) { body['reply_notifications'] = replyNotifications; } - String path = _makePostNotificationsSubscriptionPath(postUuid); + String path = _makePostCommentNotificationsSubscriptionPath( + postUuid: postUuid, postCommentId: postCommentId); return _httpService.patchJSON(_makeApiUrl(path), body: body, appendAuthorizationToken: true); } @@ -676,6 +705,15 @@ class PostsApiService { .parse(POST_NOTIFICATIONS_SUBSCRIPTION_PATH, {'postUuid': postUuid}); } + String _makePostCommentNotificationsSubscriptionPath({ + @required int postCommentId, + @required String postUuid, + }) { + return _stringTemplateService.parse( + POST_COMMENT_NOTIFICATIONS_SUBSCRIPTION_PATH, + {'postUuid': postUuid, 'postCommentId': postCommentId}); + } + String _makeMutePostCommentPath({ @required int postCommentId, @required String postUuid, diff --git a/lib/services/user.dart b/lib/services/user.dart index c7c089e6a..03d690316 100644 --- a/lib/services/user.dart +++ b/lib/services/user.dart @@ -854,6 +854,36 @@ class UserService { return Post.fromJson(json.decode(response.body)); } + Future createPostCommentNotificationsSubscription( + {@required Post post, + @required PostComment postComment, + bool reactionNotifications, + bool replyNotifications}) async { + HttpieResponse response = + await _postsApiService.createPostCommentNotificationsSubscription( + postUuid: post.uuid, + postCommentId: postComment.id, + reactionNotifications: reactionNotifications, + replyNotifications: replyNotifications); + _checkResponseIsCreated(response); + return Post.fromJson(json.decode(response.body)); + } + + Future updatePostCommentNotificationsSubscription( + {@required Post post, + @required PostComment postComment, + bool reactionNotifications, + bool replyNotifications}) async { + HttpieResponse response = + await _postsApiService.updatePostCommentNotificationsSubscription( + postUuid: post.uuid, + postCommentId: postComment.id, + reactionNotifications: reactionNotifications, + replyNotifications: replyNotifications); + _checkResponseIsOk(response); + return Post.fromJson(json.decode(response.body)); + } + Future excludeCommunityFromTopPosts(Community community) async { HttpieResponse response = await _postsApiService .excludeCommunityFromTopPosts(communityName: community.name); diff --git a/lib/widgets/tiles/actions/manage_post_comment_notifications_tile.dart b/lib/widgets/tiles/actions/manage_post_comment_notifications_tile.dart new file mode 100644 index 000000000..be4097905 --- /dev/null +++ b/lib/widgets/tiles/actions/manage_post_comment_notifications_tile.dart @@ -0,0 +1,73 @@ +import 'package:Okuna/models/post.dart'; +import 'package:Okuna/models/post_comment.dart'; +import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart'; +import 'package:Okuna/provider.dart'; +import 'package:Okuna/services/bottom_sheet.dart'; +import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/widgets/icon.dart'; +import 'package:Okuna/widgets/theming/text.dart'; +import 'package:flutter/material.dart'; + +class OBManagePostCommentNotificationsTile extends StatefulWidget { + final Post post; + final PostComment postComment; + final OnNotificationSettingsSave onNotificationSettingsSave; + + const OBManagePostCommentNotificationsTile({ + Key key, + @required this.post, + @required this.postComment, + this.onNotificationSettingsSave, + }) : super(key: key); + + @override + OBManagePostCommentNotificationsTileState createState() { + return OBManagePostCommentNotificationsTileState(); + } +} + +class OBManagePostCommentNotificationsTileState + extends State { + BottomSheetService _bottomSheetService; + LocalizationService _localizationService; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + var openbookProvider = OpenbookProvider.of(context); + _bottomSheetService = openbookProvider.bottomSheetService; + _localizationService = openbookProvider.localizationService; + + return StreamBuilder( + stream: widget.postComment.updateSubject, + initialData: widget.postComment, + builder: (BuildContext context, AsyncSnapshot snapshot) { + var postComment = snapshot.data; + + return ListTile( + leading: OBIcon(OBIcons.notifications), + title: OBText(postComment.postCommentNotificationsSubscription != null + ? _localizationService.post__manage_post_comment_notifications + : _localizationService + .post__subscribe_post_comment_notifications), + onTap: _navigateToManagePostComment, + ); + }, + ); + } + + void _navigateToManagePostComment() { + _bottomSheetService.showManagePostCommentNotifications( + context: context, + post: widget.post, + postComment: widget.postComment, + onNotificationSettingsSave: () { + if (widget.onNotificationSettingsSave != null) + widget.onNotificationSettingsSave(); + }); + } +} diff --git a/lib/widgets/tiles/actions/manage_post_notifications_tile.dart b/lib/widgets/tiles/actions/manage_post_notifications_tile.dart index 2406014b1..72e24d116 100644 --- a/lib/widgets/tiles/actions/manage_post_notifications_tile.dart +++ b/lib/widgets/tiles/actions/manage_post_notifications_tile.dart @@ -1,24 +1,19 @@ import 'package:Okuna/models/post.dart'; -import 'package:Okuna/models/post_notifications_subscription.dart'; -import 'package:Okuna/pages/home/modals/manage_notifications/manage_post_notifications.dart'; +import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart'; import 'package:Okuna/provider.dart'; +import 'package:Okuna/services/bottom_sheet.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/modal_service.dart'; -import 'package:Okuna/services/navigation_service.dart'; import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/theming/text.dart'; -import 'package:Okuna/widgets/tiles/loading_tile.dart'; import 'package:flutter/material.dart'; class OBManagePostNotificationsTile extends StatefulWidget { final Post post; final OnNotificationSettingsSave onNotificationSettingsSave; - final VoidCallback onOpenManagePostNotificationsModal; const OBManagePostNotificationsTile({ Key key, @required this.post, - this.onOpenManagePostNotificationsModal, this.onNotificationSettingsSave, }) : super(key: key); @@ -30,7 +25,7 @@ class OBManagePostNotificationsTile extends StatefulWidget { class OBManagePostNotificationsTileState extends State { - ModalService _modalService; + BottomSheetService _bottomSheetService; LocalizationService _localizationService; bool _requestInProgress; @@ -43,7 +38,7 @@ class OBManagePostNotificationsTileState @override Widget build(BuildContext context) { var openbookProvider = OpenbookProvider.of(context); - _modalService = openbookProvider.modalService; + _bottomSheetService = openbookProvider.bottomSheetService; _localizationService = openbookProvider.localizationService; return StreamBuilder( @@ -52,12 +47,9 @@ class OBManagePostNotificationsTileState builder: (BuildContext context, AsyncSnapshot snapshot) { var post = snapshot.data; - bool isReported = post.isReported ?? false; - - return OBLoadingTile( - isLoading: _requestInProgress || isReported, + return ListTile( leading: OBIcon(OBIcons.notifications), - title: OBText(widget.post.postNotificationsSubscription != null + title: OBText(post.postNotificationsSubscription != null ? _localizationService.post__manage_post_notifications : _localizationService.post__subscribe_post_notifications), onTap: _navigateToManagePost, @@ -67,8 +59,7 @@ class OBManagePostNotificationsTileState } void _navigateToManagePost() { - widget.onOpenManagePostNotificationsModal(); - _modalService.openManagePostNotifications( + _bottomSheetService.showManagePostNotifications( context: context, post: widget.post, onNotificationSettingsSave: () { From 7c7daa38576241edfc5faeb9b39f9d48cd0c25a1 Mon Sep 17 00:00:00 2001 From: Shantanu Date: Thu, 23 Apr 2020 16:18:17 +0200 Subject: [PATCH 15/39] :sparkles: notification settings modifications --- lib/models/user_notifications_settings.dart | 54 +----- .../pages/notifications_settings.dart | 179 ++---------------- lib/services/auth_api.dart | 63 +++--- lib/services/user.dart | 15 +- 4 files changed, 52 insertions(+), 259 deletions(-) diff --git a/lib/models/user_notifications_settings.dart b/lib/models/user_notifications_settings.dart index 7ddda71c5..0373b5b07 100644 --- a/lib/models/user_notifications_settings.dart +++ b/lib/models/user_notifications_settings.dart @@ -1,10 +1,6 @@ class UserNotificationsSettings { final int id; - bool postCommentNotifications; - bool postCommentReactionNotifications; - bool postCommentReplyNotifications; - bool postCommentUserMentionNotifications; - bool postUserMentionNotifications; + bool postNotifications; bool postReactionNotifications; bool followNotifications; bool connectionRequestNotifications; @@ -18,12 +14,7 @@ class UserNotificationsSettings { this.connectionConfirmedNotifications, this.connectionRequestNotifications, this.followNotifications, - this.postCommentNotifications, - this.postCommentReactionNotifications, - this.postCommentUserMentionNotifications, - this.postUserMentionNotifications, - this.postCommentReplyNotifications, - this.postReactionNotifications, + this.postNotifications, this.communityInviteNotifications, this.communityNewPostNotifications, this.userNewPostNotifications}); @@ -36,22 +27,12 @@ class UserNotificationsSettings { connectionRequestNotifications: parsedJson['connection_request_notifications'], followNotifications: parsedJson['follow_notifications'], - postCommentNotifications: parsedJson['post_comment_notifications'], - postCommentReactionNotifications: - parsedJson['post_comment_reaction_notifications'], - postCommentReplyNotifications: - parsedJson['post_comment_reply_notifications'], - postCommentUserMentionNotifications: - parsedJson['post_comment_user_mention_notifications'], - postUserMentionNotifications: - parsedJson['post_user_mention_notifications'], - postReactionNotifications: parsedJson['post_reaction_notifications'], + postNotifications: parsedJson['post_notifications'], communityInviteNotifications: parsedJson['community_invite_notifications'], communityNewPostNotifications: parsedJson['community_new_post_notifications'], - userNewPostNotifications: - parsedJson['user_new_post_notifications'], + userNewPostNotifications: parsedJson['user_new_post_notifications'], ); } @@ -61,12 +42,7 @@ class UserNotificationsSettings { 'connections_confirmed_notifications': connectionConfirmedNotifications, 'connection_request_notifications': connectionRequestNotifications, 'follow_notifications': followNotifications, - 'post_comment_notifications': postCommentNotifications, - 'post_comment_reaction_notifications': postCommentReactionNotifications, - 'post_comment_user_mention_notifications': postCommentUserMentionNotifications, - 'post_user_mention_notifications': postUserMentionNotifications, - 'post_comment_reply_notifications': postCommentReplyNotifications, - 'post_reaction_notifications': postReactionNotifications, + 'post_notifications': postNotifications, 'community_invite_notifications': communityInviteNotifications, 'community_new_post_notifications': communityNewPostNotifications, 'user_new_post_notifications': userNewPostNotifications, @@ -81,24 +57,8 @@ class UserNotificationsSettings { connectionRequestNotifications = json['connection_request_notifications']; if (json.containsKey('follow_notifications')) followNotifications = json['follow_notifications']; - if (json.containsKey('post_comment_notifications')) - postCommentNotifications = json['post_comment_notifications']; - - if (json.containsKey('post_comment_reaction_notifications')) - postCommentReactionNotifications = - json['post_comment_reaction_notifications']; - - if (json.containsKey('post_comment_reply_notifications')) - postCommentReplyNotifications = json['post_comment_reply_notifications']; - - if (json.containsKey('post_comment_user_mention_notifications')) - postCommentUserMentionNotifications = json['post_comment_user_mention_notifications']; - - if (json.containsKey('post_user_mention_notifications')) - postUserMentionNotifications = json['post_user_mention_notifications']; - - if (json.containsKey('post_reaction_notifications')) - postReactionNotifications = json['post_reaction_notifications']; + if (json.containsKey('post_notifications')) + postNotifications = json['post_notifications']; if (json.containsKey('community_invite_notifications')) communityInviteNotifications = json['community_invite_notifications']; if (json.containsKey('community_new_post_notifications')) diff --git a/lib/pages/home/pages/notifications/pages/notifications_settings.dart b/lib/pages/home/pages/notifications/pages/notifications_settings.dart index 14cb99b3a..ca6f489de 100644 --- a/lib/pages/home/pages/notifications/pages/notifications_settings.dart +++ b/lib/pages/home/pages/notifications/pages/notifications_settings.dart @@ -35,12 +35,7 @@ class OBNotificationsSettingsPageState bool _pushNotifications; - bool _postCommentNotifications; - bool _postCommentReactionNotifications; - bool _postCommentReplyNotifications; - bool _postCommentUserMentionNotifications; - bool _postUserMentionNotifications; - bool _postReactionNotifications; + bool _postNotifications; bool _followNotifications; bool _connectionRequestNotifications; bool _communityInviteNotifications; @@ -52,17 +47,12 @@ class OBNotificationsSettingsPageState super.initState(); _needsBootstrap = true; _bootstrapInProgress = true; - _postCommentNotifications = true; - _postCommentReactionNotifications = true; - _postCommentReplyNotifications = true; - _postReactionNotifications = true; + _postNotifications = true; _followNotifications = true; _connectionRequestNotifications = true; _communityInviteNotifications = true; _communityNewPostNotifications = true; _userNewPostNotifications = true; - _postUserMentionNotifications = true; - _postCommentUserMentionNotifications = true; } @override @@ -150,68 +140,14 @@ class OBNotificationsSettingsPageState ), listItemSeparator, OBToggleField( - key: Key('Post comments'), - value: _postCommentNotifications, - title: _localizationService.trans('notifications__comment_title'), - subtitle: - OBText(_localizationService.trans('notifications__comment_desc')), - onChanged: _setPostCommentNotifications, - onTap: _togglePostCommentNotifications, - hasDivider: false, - ), - OBToggleField( - key: Key('Post comments replies'), - value: _postCommentReplyNotifications, - title: - _localizationService.trans('notifications__comment_reply_title'), + key: Key('Post notifications'), + value: _postNotifications, + title: _localizationService.notifications__post_notifications_title, subtitle: OBText( - _localizationService.trans('notifications__comment_reply_desc')), - onChanged: _setPostCommentReplyNotifications, - onTap: _togglePostCommentReplyNotifications, - hasDivider: false, - ), - OBToggleField( - key: Key('Post comment user mentions'), - value: _postCommentUserMentionNotifications, - title: _localizationService.notifications__comment_user_mention_title, - subtitle: OBText( - _localizationService.notifications__comment_user_mention_desc), - onChanged: _setPostCommentUserMentionNotifications, - onTap: _togglePostCommentUserMentionNotifications, - hasDivider: false, - ), - OBToggleField( - key: Key('Post user mentions'), - value: _postUserMentionNotifications, - title: _localizationService.notifications__post_user_mention_title, - subtitle: OBText( - _localizationService.notifications__post_user_mention_desc), - onChanged: _setPostUserMentionNotifications, - onTap: _togglePostUserMentionNotifications, - hasDivider: false, - ), - OBToggleField( - key: Key('Post comment reactions'), - value: _postCommentReactionNotifications, - title: _localizationService - .trans('notifications__comment_reaction_title'), - subtitle: OBText(_localizationService - .trans('notifications__comment_reaction_desc')), - onChanged: _setPostCommentReactionNotifications, - onTap: _togglePostCommentReactionNotifications, - hasDivider: false, - ), - listItemSeparator, - OBToggleField( - key: Key('Post reaction'), - value: _postReactionNotifications, - title: - _localizationService.trans('notifications__post_reaction_title'), - subtitle: OBText( - _localizationService.trans('notifications__post_reaction_desc'), + _localizationService.notifications__post_notifications_desc, ), - onChanged: _setPostReactionNotifications, - onTap: _togglePostReactionNotifications, + onChanged: _setPostNotifications, + onTap: _togglePostNotifications, hasDivider: false, ), OBToggleField( @@ -229,8 +165,8 @@ class OBNotificationsSettingsPageState key: Key('Community new post'), value: _communityNewPostNotifications, title: _localizationService.notifications__community_new_post_title, - subtitle: OBText(_localizationService - .notifications__community_new_post_desc), + subtitle: OBText( + _localizationService.notifications__community_new_post_desc), onChanged: _setCommunityNewPostNotifications, onTap: _toggleCommunityNewPostNotifications, hasDivider: false, @@ -239,8 +175,8 @@ class OBNotificationsSettingsPageState key: Key('User new post'), value: _userNewPostNotifications, title: _localizationService.notifications__user_new_post_title, - subtitle: OBText(_localizationService - .notifications__user_new_post_desc), + subtitle: + OBText(_localizationService.notifications__user_new_post_desc), onChanged: _setUserNewPostNotifications, onTap: _toggleUserNewPostNotifications, hasDivider: false, @@ -332,74 +268,13 @@ class OBNotificationsSettingsPageState _submitNotificationsSettings(); } - void _togglePostCommentNotifications() { - _setPostCommentNotifications(!_postCommentNotifications); - } - - void _setPostCommentNotifications(bool newValue) { - setState(() { - _postCommentNotifications = newValue; - }); - - _submitNotificationsSettings(); - } - - void _togglePostCommentReactionNotifications() { - _setPostCommentReactionNotifications(!_postCommentReactionNotifications); - } - - void _setPostCommentReactionNotifications(bool newValue) { - setState(() { - _postCommentReactionNotifications = newValue; - }); - - _submitNotificationsSettings(); - } - - void _togglePostCommentReplyNotifications() { - _setPostCommentReplyNotifications(!_postCommentReplyNotifications); - } - - void _setPostCommentReplyNotifications(bool newValue) { - setState(() { - _postCommentReplyNotifications = newValue; - }); - - _submitNotificationsSettings(); - } - - void _togglePostCommentUserMentionNotifications() { - _setPostCommentUserMentionNotifications( - !_postCommentUserMentionNotifications); - } - - void _setPostCommentUserMentionNotifications(bool newValue) { - setState(() { - _postCommentUserMentionNotifications = newValue; - }); - - _submitNotificationsSettings(); - } - - void _togglePostUserMentionNotifications() { - _setPostUserMentionNotifications(!_postUserMentionNotifications); - } - - void _setPostUserMentionNotifications(bool newValue) { - setState(() { - _postUserMentionNotifications = newValue; - }); - - _submitNotificationsSettings(); - } - - void _togglePostReactionNotifications() { - _setPostReactionNotifications(!_postReactionNotifications); + void _togglePostNotifications() { + _setPostNotifications(!_postNotifications); } - void _setPostReactionNotifications(bool newValue) { + void _setPostNotifications(bool newValue) { setState(() { - _postReactionNotifications = newValue; + _postNotifications = newValue; }); _submitNotificationsSettings(); @@ -437,12 +312,7 @@ class OBNotificationsSettingsPageState try { _userService.updateAuthenticatedUserNotificationsSettings( followNotifications: _followNotifications, - postCommentNotifications: _postCommentNotifications, - postCommentReplyNotifications: _postCommentReplyNotifications, - postCommentUserMentionNotifications: _postCommentUserMentionNotifications, - postUserMentionNotifications: _postUserMentionNotifications, - postCommentReactionNotifications: _postCommentReactionNotifications, - postReactionNotifications: _postReactionNotifications, + postNotifications: _postNotifications, connectionRequestNotifications: _connectionRequestNotifications, communityNewPostNotifications: _communityNewPostNotifications, userNewPostNotifications: _userNewPostNotifications, @@ -472,24 +342,13 @@ class OBNotificationsSettingsPageState setState(() { _connectionRequestNotifications = notificationSettings.connectionRequestNotifications; - _postCommentNotifications = notificationSettings.postCommentNotifications; - _postCommentReactionNotifications = - notificationSettings.postCommentReactionNotifications; - _postCommentUserMentionNotifications = - notificationSettings.postCommentUserMentionNotifications; - _postUserMentionNotifications = - notificationSettings.postUserMentionNotifications; - _postCommentReplyNotifications = - notificationSettings.postCommentReplyNotifications; - _postReactionNotifications = - notificationSettings.postReactionNotifications; + _postNotifications = notificationSettings.postNotifications; _followNotifications = notificationSettings.followNotifications; _communityInviteNotifications = notificationSettings.communityInviteNotifications; _communityNewPostNotifications = notificationSettings.communityNewPostNotifications; - _userNewPostNotifications = - notificationSettings.userNewPostNotifications; + _userNewPostNotifications = notificationSettings.userNewPostNotifications; }); } diff --git a/lib/services/auth_api.dart b/lib/services/auth_api.dart index 5217bc22a..3e857bb70 100644 --- a/lib/services/auth_api.dart +++ b/lib/services/auth_api.dart @@ -30,7 +30,8 @@ class AuthApiService { static const SEARCH_LINKED_USERS_PATH = 'api/auth/linked-users/search/'; static const GET_BLOCKED_USERS_PATH = 'api/auth/blocked-users/'; static const SEARCH_BLOCKED_USERS_PATH = 'api/auth/blocked-users/search/'; - static const ENABLE_NEW_POST_NOTIFICATIONS_FOR_USER_PATH = 'api/auth/users/{userUsername}/notifications/subscribe/new-post/'; + static const ENABLE_NEW_POST_NOTIFICATIONS_FOR_USER_PATH = + 'api/auth/users/{userUsername}/notifications/subscribe/new-post/'; static const BLOCK_USER_PATH = 'api/auth/users/{userUsername}/block/'; static const UNBLOCK_USER_PATH = 'api/auth/users/{userUsername}/unblock/'; static const GET_FOLLOWERS_PATH = 'api/auth/followers/'; @@ -167,12 +168,10 @@ class AuthApiService { body: body); } - Future verifyRegisterToken( - {@required String token}) { + Future verifyRegisterToken({@required String token}) { Map body = {'token': token}; - return _httpService.post('$apiURL$VERIFY_REGISTER_TOKEN', - body: body); + return _httpService.post('$apiURL$VERIFY_REGISTER_TOKEN', body: body); } Future getUserWithAuthToken(String authToken) { @@ -267,14 +266,20 @@ class AuthApiService { return _httpService.post(_makeApiUrl(path), appendAuthorizationToken: true); } - Future enableNewPostNotificationsForUserWithUsername(String userUsername) { - String path = _makeEnableNewPostNotificationsForUserWithUsernamePath(userUsername); - return _httpService.putJSON(_makeApiUrl(path), appendAuthorizationToken: true); + Future enableNewPostNotificationsForUserWithUsername( + String userUsername) { + String path = + _makeEnableNewPostNotificationsForUserWithUsernamePath(userUsername); + return _httpService.putJSON(_makeApiUrl(path), + appendAuthorizationToken: true); } - Future disableNewPostNotificationsForUserWithUsername(String userUsername) { - String path = _makeEnableNewPostNotificationsForUserWithUsernamePath(userUsername); - return _httpService.delete(_makeApiUrl(path), appendAuthorizationToken: true); + Future disableNewPostNotificationsForUserWithUsername( + String userUsername) { + String path = + _makeEnableNewPostNotificationsForUserWithUsernamePath(userUsername); + return _httpService.delete(_makeApiUrl(path), + appendAuthorizationToken: true); } Future searchFollowers({@required String query, int count}) { @@ -353,12 +358,7 @@ class AuthApiService { } Future updateAuthenticatedUserNotificationsSettings({ - bool postCommentNotifications, - bool postCommentReplyNotifications, - bool postCommentReactionNotifications, - bool postCommentUserMentionNotifications, - bool postUserMentionNotifications, - bool postReactionNotifications, + bool postNotifications, bool followNotifications, bool connectionRequestNotifications, bool connectionConfirmedNotifications, @@ -368,25 +368,8 @@ class AuthApiService { }) { Map body = {}; - if (postCommentNotifications != null) - body['post_comment_notifications'] = postCommentNotifications; - - if (postCommentReplyNotifications != null) - body['post_comment_reply_notifications'] = postCommentReplyNotifications; - - if (postCommentUserMentionNotifications != null) - body['post_comment_user_mention_notifications'] = - postCommentUserMentionNotifications; - - if (postUserMentionNotifications != null) - body['post_user_mention_notifications'] = postUserMentionNotifications; - - if (postCommentReactionNotifications != null) - body['post_comment_reaction_notifications'] = - postCommentReactionNotifications; - - if (postReactionNotifications != null) - body['post_reaction_notifications'] = postReactionNotifications; + if (postNotifications != null) + body['post_notifications'] = postNotifications; if (followNotifications != null) body['follow_notifications'] = followNotifications; @@ -456,9 +439,11 @@ class AuthApiService { .parse(UNBLOCK_USER_PATH, {'userUsername': username}); } - String _makeEnableNewPostNotificationsForUserWithUsernamePath(String username) { - return _stringTemplateService - .parse(ENABLE_NEW_POST_NOTIFICATIONS_FOR_USER_PATH, {'userUsername': username}); + String _makeEnableNewPostNotificationsForUserWithUsernamePath( + String username) { + return _stringTemplateService.parse( + ENABLE_NEW_POST_NOTIFICATIONS_FOR_USER_PATH, + {'userUsername': username}); } String _makeReportUserPath({@required username}) { diff --git a/lib/services/user.dart b/lib/services/user.dart index 03d690316..92f424910 100644 --- a/lib/services/user.dart +++ b/lib/services/user.dart @@ -2020,12 +2020,7 @@ class UserService { Future updateAuthenticatedUserNotificationsSettings({ - bool postCommentNotifications, - bool postCommentReplyNotifications, - bool postCommentReactionNotifications, - bool postCommentUserMentionNotifications, - bool postUserMentionNotifications, - bool postReactionNotifications, + bool postNotifications, bool followNotifications, bool connectionRequestNotifications, bool connectionConfirmedNotifications, @@ -2035,13 +2030,7 @@ class UserService { }) async { HttpieResponse response = await _authApiService.updateAuthenticatedUserNotificationsSettings( - postCommentNotifications: postCommentNotifications, - postCommentReplyNotifications: postCommentReplyNotifications, - postCommentUserMentionNotifications: - postCommentUserMentionNotifications, - postUserMentionNotifications: postUserMentionNotifications, - postCommentReactionNotifications: postCommentReactionNotifications, - postReactionNotifications: postReactionNotifications, + postNotifications: postNotifications, followNotifications: followNotifications, connectionConfirmedNotifications: connectionConfirmedNotifications, communityInviteNotifications: communityInviteNotifications, From aa870425b996059b5741286bfe806a0bd32817af Mon Sep 17 00:00:00 2001 From: Shantanu Date: Tue, 28 Apr 2020 17:11:37 +0200 Subject: [PATCH 16/39] :bug: fix bug --- .../widgets/manage_notifications.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart b/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart index e67febc94..03c2b6231 100644 --- a/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart +++ b/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart @@ -58,7 +58,7 @@ class OBManageNotificationsState extends State { }); } - void _toggleLocalStateIsMuted() { + void _toggleMute() { setState(() { _isMuted = !_isMuted; }); @@ -75,8 +75,10 @@ class OBManageNotificationsState extends State { isDisabled: notificationSetting.isDisabled, title: notificationSetting.localizedTitle, subtitle: OBText(notificationSetting.localizedDesc), - onChanged: (bool newValue) => - notificationSetting.value = newValue, + onChanged: (bool newValue) { + _toggleNotificationSetting(notificationSetting); + _onWantsToSaveSettings(); + }, onTap: () { _toggleNotificationSetting(notificationSetting); _onWantsToSaveSettings(); @@ -130,7 +132,7 @@ class OBManageNotificationsState extends State { void _onWantsToToggleMute() async { try { widget.onWantsToToggleMute(); - _toggleLocalStateIsMuted(); + _toggleMute(); } catch (error) { print('Manage Notifications Error while toggling mute'); } From 0a93b3fed5af4a50d57883e1cd3303048e03cdf0 Mon Sep 17 00:00:00 2001 From: Shantanu Date: Tue, 28 Apr 2020 18:18:30 +0200 Subject: [PATCH 17/39] :globe_with_meridians: localization stringss --- assets/i18n/en/notifications.arb | 58 ++-------------------------- assets/i18n/en/post.arb | 33 ++++++++++++++-- lib/services/localization.dart | 66 +------------------------------- 3 files changed, 34 insertions(+), 123 deletions(-) diff --git a/assets/i18n/en/notifications.arb b/assets/i18n/en/notifications.arb index b49f981d3..46facd029 100644 --- a/assets/i18n/en/notifications.arb +++ b/assets/i18n/en/notifications.arb @@ -44,63 +44,13 @@ "type": "text", "placeholders": {} }, - "comment_title": "Post comment", - "@comment_title": { + "post_notifications_title": "Post notifications", + "@post_notifications_title": { "type": "text", "placeholders": {} }, - "comment_desc": "Be notified when someone comments on one of your posts or one you also commented", - "@comment_desc": { - "type": "text", - "placeholders": {} - }, - "comment_reply_title": "Post comment reply", - "@comment_reply_title": { - "type": "text", - "placeholders": {} - }, - "comment_reply_desc": "Be notified when someone replies to one of your comments or one you also replied to", - "@comment_reply_desc": { - "type": "text", - "placeholders": {} - }, - "comment_user_mention_title": "Post comment mention", - "@comment_user_mention_title": { - "type": "text", - "placeholders": {} - }, - "comment_user_mention_desc": "Be notified when someone mentions you on one of their comments", - "@comment_user_mention_desc": { - "type": "text", - "placeholders": {} - }, - "post_user_mention_title": "Post mention", - "@post_user_mention_title": { - "type": "text", - "placeholders": {} - }, - "post_user_mention_desc": "Be notified when someone mentions you on one of their posts", - "@post_user_mention_desc": { - "type": "text", - "placeholders": {} - }, - "comment_reaction_title": "Post comment reaction", - "@comment_reaction_title": { - "type": "text", - "placeholders": {} - }, - "comment_reaction_desc": "Be notified when someone reacts to one of your post commments", - "@comment_reaction_desc": { - "type": "text", - "placeholders": {} - }, - "post_reaction_title": "Post reaction", - "@post_reaction_title": { - "type": "text", - "placeholders": {} - }, - "post_reaction_desc": "Be notified when someone reacts to one of your posts", - "@post_reaction_desc": { + "post_notifications_desc": "Be notified of all post related notifications - comments, replies, reactions and user mentions", + "@post_notifications_desc": { "type": "text", "placeholders": {} }, diff --git a/assets/i18n/en/post.arb b/assets/i18n/en/post.arb index 3c901ed95..c8b402467 100644 --- a/assets/i18n/en/post.arb +++ b/assets/i18n/en/post.arb @@ -528,8 +528,8 @@ "type": "text", "placeholders": {} }, - "post_notifications_title_text": "Post notifications", - "@post_notifications_title_text": { + "manage_post_comment_notifications": "Manage comment notifications", + "@manage_post_comment_notifications": { "type": "text", "placeholders": {} }, @@ -538,8 +538,8 @@ "type": "text", "placeholders": {} }, - "save_post_notifications_text": "Save", - "@save_post_notifications_text": { + "subscribe_post_comment_notifications": "Subscribe to comment notifications", + "@subscribe_post_comment_notifications": { "type": "text", "placeholders": {} }, @@ -553,6 +553,16 @@ "type": "text", "placeholders": {} }, + "mute_post_comment_notifications_text": "Mute comment", + "@mute_post_comment_notifications_text": { + "type": "text", + "placeholders": {} + }, + "unmute_post_comment_notifications_text": "Unmute comment", + "@unmute_post_comment_notifications_text": { + "type": "text", + "placeholders": {} + }, "manage_notifications_comments_title": "Comments", "@manage_notifications_comments_title": { "type": "text", @@ -583,11 +593,26 @@ "type": "text", "placeholders": {} }, + "manage_notifications_reactions_post_comment_desc": "Get notifications when someone reacts to your comment", + "@manage_notifications_reactions_post_comment_desc": { + "type": "text", + "placeholders": {} + }, "manage_notifications_replies_title": "Replies", "@manage_notifications_replies_title": { "type": "text", "placeholders": {} }, + "manage_notifications_replies_desc": "Get notifications when someone replies to comments on this post", + "@manage_notifications_replies_desc": { + "type": "text", + "placeholders": {} + }, + "manage_notifications_replies_post_comment_desc": "Get notifications when someone replies to this comment", + "@manage_notifications_replies_post_comment_desc": { + "type": "text", + "placeholders": {} + }, "manage_notifications_successfully_saved": "Notification settings saved", "@manage_notifications_successfully_saved": { "type": "text", diff --git a/lib/services/localization.dart b/lib/services/localization.dart index a63684597..f0e08c827 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -2272,7 +2272,7 @@ class LocalizationService { String get post__manage_notifications_replies_desc { return Intl.message( "Get notifications when someone replies to comments on this post", - name: 'post__manage_notifications_reactions_desc'); + name: 'post__manage_notifications_replies_desc'); } String get post__manage_notifications_replies_post_comment_desc { @@ -3460,70 +3460,6 @@ class LocalizationService { name: 'notifications__connection_desc'); } - String get notifications__comment_title { - return Intl.message("Post comment", name: 'notifications__comment_title'); - } - - String get notifications__comment_desc { - return Intl.message( - "Be notified when someone comments on one of your posts or one you also commented", - name: 'notifications__comment_desc'); - } - - String get notifications__comment_reply_title { - return Intl.message("Post comment reply", - name: 'notifications__comment_reply_title'); - } - - String get notifications__comment_reply_desc { - return Intl.message( - "Be notified when someone replies to one of your comments or one you also replied to", - name: 'notifications__comment_reply_desc'); - } - - String get notifications__comment_user_mention_title { - return Intl.message("Post comment mention", - name: 'notifications__comment_user_mention_title'); - } - - String get notifications__comment_user_mention_desc { - return Intl.message( - "Be notified when someone mentions you on one of their comments", - name: 'notifications__comment_user_mention_desc'); - } - - String get notifications__post_user_mention_title { - return Intl.message("Post mention", - name: 'notifications__post_user_mention_title'); - } - - String get notifications__post_user_mention_desc { - return Intl.message( - "Be notified when someone mentions you on one of their posts", - name: 'notifications__post_user_mention_desc'); - } - - String get notifications__comment_reaction_title { - return Intl.message("Post comment reaction", - name: 'notifications__comment_reaction_title'); - } - - String get notifications__comment_reaction_desc { - return Intl.message( - "Be notified when someone reacts to one of your post commments", - name: 'notifications__comment_reaction_desc'); - } - - String get notifications__post_reaction_title { - return Intl.message("Post reaction", - name: 'notifications__post_reaction_title'); - } - - String get notifications__post_reaction_desc { - return Intl.message("Be notified when someone reacts to one of your posts", - name: 'notifications__post_reaction_desc'); - } - String get notifications__post_notifications_title { return Intl.message("Post notifications", name: 'notifications__post_notifications_title'); From 9f4e9ef54af66f7ac57865a5bdf8a66f9ea8fbaf Mon Sep 17 00:00:00 2001 From: Shantanu Date: Wed, 29 Apr 2020 11:21:28 +0200 Subject: [PATCH 18/39] :sparkles: allow reply to reply --- lib/models/user.dart | 51 +++++++++++-------- .../post_comment_reply_expanded.dart | 9 +++- .../widgets/post_comment_actions.dart | 17 ++++--- lib/services/modal_service.dart | 2 + 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/lib/models/user.dart b/lib/models/user.dart index e12e77565..7d549226e 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -58,9 +58,9 @@ class User extends UpdatableModel { storage: UpdatableModelSimpleStorage(size: 10))); static final maxSessionUsersFactory = UserFactory( - cache: - SimpleCache(storage: UpdatableModelSimpleStorage(size: UpdatableModelSimpleStorage.MAX_INT)) - ); + cache: SimpleCache( + storage: UpdatableModelSimpleStorage( + size: UpdatableModelSimpleStorage.MAX_INT))); factory User.fromJson(Map json, {bool storeInSessionCache = false, bool storeInMaxSessionCache = false}) { @@ -69,15 +69,17 @@ class User extends UpdatableModel { int userId = json['id']; User user = maxSessionUsersFactory.getItemWithIdFromCache(userId) ?? - navigationUsersFactory.getItemWithIdFromCache(userId) ?? - sessionUsersFactory.getItemWithIdFromCache(userId); + navigationUsersFactory.getItemWithIdFromCache(userId) ?? + sessionUsersFactory.getItemWithIdFromCache(userId); if (user != null) { user.update(json); return user; } - return storeInMaxSessionCache ? maxSessionUsersFactory.fromJson(json) : storeInSessionCache - ? sessionUsersFactory.fromJson(json) - : navigationUsersFactory.fromJson(json); + return storeInMaxSessionCache + ? maxSessionUsersFactory.fromJson(json) + : storeInSessionCache + ? sessionUsersFactory.fromJson(json) + : navigationUsersFactory.fromJson(json); } Map toJson() { @@ -96,7 +98,8 @@ class User extends UpdatableModel { 'unread_notifications_count': unreadNotificationsCount, 'posts_count': postsCount, 'invite_count': inviteCount, - 'pending_communities_moderated_objects_count': pendingCommunitiesModeratedObjectsCount, + 'pending_communities_moderated_objects_count': + pendingCommunitiesModeratedObjectsCount, 'active_moderation_penalties_count': activeModerationPenaltiesCount, 'are_guidelines_accepted': areGuidelinesAccepted, 'are_new_post_notifications_enabled': areNewPostNotificationsEnabled, @@ -109,10 +112,18 @@ class User extends UpdatableModel { 'is_fully_connected': isFullyConnected, 'is_pending_connection_confirmation': isPendingConnectionConfirmation, 'is_member_of_communities': isMemberOfCommunities, - 'connected_circles': connectedCircles?.circles?.map((Circle circle) => circle.toJson())?.toList(), - 'follow_lists': followLists?.lists?.map((FollowsList followList) => followList.toJson())?.toList(), - 'communities_memberships': communitiesMemberships?.communityMemberships?.map((CommunityMembership membership) => membership.toJson())?.toList(), - 'communities_invites' : communitiesInvites?.communityInvites?.map((CommunityInvite invite) => invite.toJson())?.toList(), + 'connected_circles': connectedCircles?.circles + ?.map((Circle circle) => circle.toJson()) + ?.toList(), + 'follow_lists': followLists?.lists + ?.map((FollowsList followList) => followList.toJson()) + ?.toList(), + 'communities_memberships': communitiesMemberships?.communityMemberships + ?.map((CommunityMembership membership) => membership.toJson()) + ?.toList(), + 'communities_invites': communitiesInvites?.communityInvites + ?.map((CommunityInvite invite) => invite.toJson()) + ?.toList(), }; } @@ -163,7 +174,8 @@ class User extends UpdatableModel { void updateFromJson(Map json) { if (json.containsKey('username')) username = json['username']; if (json.containsKey('uuid')) uuid = json['uuid']; - if (json.containsKey('date_joined')) dateJoined = navigationUsersFactory.parseDateJoined(json['date_joined']); + if (json.containsKey('date_joined')) + dateJoined = navigationUsersFactory.parseDateJoined(json['date_joined']); if (json.containsKey('are_guidelines_accepted')) areGuidelinesAccepted = json['are_guidelines_accepted']; if (json.containsKey('email')) email = json['email']; @@ -199,7 +211,9 @@ class User extends UpdatableModel { unreadNotificationsCount = json['unread_notifications_count']; if (json.containsKey('posts_count')) postsCount = json['posts_count']; if (json.containsKey('invite_count')) inviteCount = json['invite_count']; - if (json.containsKey('are_new_post_notifications_enabled')) areNewPostNotificationsEnabled = json['are_new_post_notifications_enabled']; + if (json.containsKey('are_new_post_notifications_enabled')) + areNewPostNotificationsEnabled = + json['are_new_post_notifications_enabled']; if (json.containsKey('is_following')) isFollowing = json['is_following']; if (json.containsKey('is_followed')) isFollowed = json['is_followed']; if (json.containsKey('is_connected')) isConnected = json['is_connected']; @@ -597,10 +611,6 @@ class User extends UpdatableModel { return loggedInUser.id != postCommenter.id; } - bool canReplyPostComment(PostComment postComment) { - return postComment.parentComment == null; - } - bool canDeletePostComment(Post post, PostComment postComment) { User loggedInUser = this; User postCommenter = postComment.commenter; @@ -651,7 +661,8 @@ class UserFactory extends UpdatableModelFactory { followingCount: json['following_count'], isFollowing: json['is_following'], isFollowed: json['is_followed'], - areNewPostNotificationsEnabled: json['are_new_post_notifications_enabled'], + areNewPostNotificationsEnabled: + json['are_new_post_notifications_enabled'], isConnected: json['is_connected'], isGlobalModerator: json['is_global_moderator'], isBlocked: json['is_blocked'], diff --git a/lib/pages/home/modals/post_comment/post_comment_reply_expanded.dart b/lib/pages/home/modals/post_comment/post_comment_reply_expanded.dart index c734d8d08..311ea99ea 100644 --- a/lib/pages/home/modals/post_comment/post_comment_reply_expanded.dart +++ b/lib/pages/home/modals/post_comment/post_comment_reply_expanded.dart @@ -26,6 +26,7 @@ import 'package:flutter/material.dart'; class OBPostCommentReplyExpandedModal extends StatefulWidget { final Post post; final PostComment postComment; + final PostComment postCommentReply; final Function(PostComment) onReplyAdded; final Function(PostComment) onReplyDeleted; @@ -33,6 +34,7 @@ class OBPostCommentReplyExpandedModal extends StatefulWidget { {Key key, this.post, this.postComment, + this.postCommentReply, this.onReplyAdded, this.onReplyDeleted}) : super(key: key); @@ -81,15 +83,20 @@ class OBPostCommentReplyExpandedModalState @override void bootstrap() { super.bootstrap(); + String defaultControllerText = widget.postCommentReply != null + ? '@${widget.postCommentReply.commenter.username} ' + : null; _textController = DraftTextEditingController.comment(widget.post.id, commentId: widget.postComment != null ? widget.postComment.id : null, + text: defaultControllerText, draftService: _draftService); _textController.addListener(_onPostCommentTextChanged); setAutocompleteTextController(_textController); String hintText = _localizationService.post__comment_reply_expanded_reply_hint_text; + _postCommentItemsWidgets = [ OBCreatePostText(controller: _textController, hintText: hintText) ]; @@ -195,7 +202,7 @@ class OBPostCommentReplyExpandedModalState children: [ OBPostComment( post: widget.post, - postComment: widget.postComment, + postComment: widget.postCommentReply ?? widget.postComment, showActions: false, showReactions: false, showReplies: false, diff --git a/lib/pages/home/pages/post_comments/widgets/post_comment/widgets/post_comment_actions.dart b/lib/pages/home/pages/post_comments/widgets/post_comment/widgets/post_comment_actions.dart index df788bc9d..6ff201fbf 100644 --- a/lib/pages/home/pages/post_comments/widgets/post_comment/widgets/post_comment_actions.dart +++ b/lib/pages/home/pages/post_comments/widgets/post_comment/widgets/post_comment_actions.dart @@ -84,10 +84,7 @@ class OBPostCommentActionsState extends State { _buildReactButton(), ]; - if (widget.showReplyAction && - _userService - .getLoggedInUser() - .canReplyPostComment(widget.postComment)) { + if (widget.showReplyAction) { actionItems.add(_buildReplyButton()); } @@ -154,7 +151,6 @@ class OBPostCommentActionsState extends State { } Widget _buildReplyButton() { - return Padding( padding: const EdgeInsets.only(top: 10, bottom: 10, right: 10), child: GestureDetector( @@ -178,13 +174,17 @@ class OBPostCommentActionsState extends State { PostComment comment = await _modalService.openExpandedReplyCommenter( context: context, post: widget.post, - postComment: widget.postComment, + postComment: widget.postComment.parentComment ?? + widget.postComment, // if reply to reply use parent comment + postCommentReply: widget.postComment.parentComment != null + ? widget.postComment + : null, onReplyDeleted: widget.onReplyDeleted, onReplyAdded: widget.onReplyAdded); if (comment != null) { await _navigationService.navigateToPostCommentReplies( post: widget.post, - postComment: widget.postComment, + postComment: widget.postComment.parentComment ?? widget.postComment, onReplyAdded: widget.onReplyAdded, onReplyDeleted: widget.onReplyDeleted, context: context); @@ -244,7 +244,8 @@ class OBPostCommentActionsState extends State { String errorMessage = await error.toHumanReadableMessage(); _toastService.error(message: errorMessage, context: context); } else { - _toastService.error(message: _localizationService.error__unknown_error, context: context); + _toastService.error( + message: _localizationService.error__unknown_error, context: context); throw error; } } diff --git a/lib/services/modal_service.dart b/lib/services/modal_service.dart index 535dd3fbc..7d2ca747f 100644 --- a/lib/services/modal_service.dart +++ b/lib/services/modal_service.dart @@ -106,6 +106,7 @@ class ModalService { Future openExpandedReplyCommenter( {@required BuildContext context, @required PostComment postComment, + PostComment postCommentReply, @required Post post, @required Function(PostComment) onReplyAdded, @required Function(PostComment) onReplyDeleted}) async { @@ -117,6 +118,7 @@ class ModalService { child: OBPostCommentReplyExpandedModal( post: post, postComment: postComment, + postCommentReply: postCommentReply, onReplyAdded: onReplyAdded, onReplyDeleted: onReplyDeleted), ); From 0eb0e403af20849a496df0a36712d0b0d2660d7d Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Sun, 3 May 2020 15:59:44 +0200 Subject: [PATCH 19/39] :bug: fix flutter breaking changes --- lib/main.dart | 11 +---- lib/services/dialog.dart | 2 +- lib/widgets/routes/fadein_material_route.dart | 15 ------- .../widgets/chewie/chewie_player.dart | 41 ++++++++++-------- pubspec.lock | 43 ++++++++++++++----- 5 files changed, 57 insertions(+), 55 deletions(-) delete mode 100644 lib/widgets/routes/fadein_material_route.dart diff --git a/lib/main.dart b/lib/main.dart index 1b203ef7c..e73b9ff42 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -300,16 +300,7 @@ Future main() async { app = MyApp(); runApp(app); }, - HttpieOverrides(), - onError: (error, stackTrace) async { - if (isOnDesktop) { - DesktopErrorReporting.reportError(error, stackTrace); - return; - } - SentryClient sentryClient = - app.openbookProviderKey.currentState.sentryClient; - await _reportError(error, stackTrace, sentryClient); - }); + HttpieOverrides()); } /// Reports [error] along with its [stackTrace] to Sentry.io. diff --git a/lib/services/dialog.dart b/lib/services/dialog.dart index 50d1929e7..0b225eebb 100644 --- a/lib/services/dialog.dart +++ b/lib/services/dialog.dart @@ -38,8 +38,8 @@ class DialogService { enableAlpha: enableAlpha, pickerColor: initialColor, onColorChanged: onColorChanged, - enableLabel: false, pickerAreaHeightPercent: 0.8, + showLabel: true, ), ), context: context); diff --git a/lib/widgets/routes/fadein_material_route.dart b/lib/widgets/routes/fadein_material_route.dart deleted file mode 100644 index 5e9795f5c..000000000 --- a/lib/widgets/routes/fadein_material_route.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class OBFadeInMaterialPageRoute extends MaterialPageRoute { - OBFadeInMaterialPageRoute({ WidgetBuilder builder, RouteSettings settings, bool fullscreenDialog }) - : super(builder: builder, settings: settings, fullscreenDialog: fullscreenDialog); - - @override - Widget buildTransitions(BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child) { - if (settings.isInitialRoute) return child; - return new FadeTransition(opacity: animation, child: child); - } -} \ No newline at end of file diff --git a/lib/widgets/video_player/widgets/chewie/chewie_player.dart b/lib/widgets/video_player/widgets/chewie/chewie_player.dart index a1d2bc26c..c9166bc49 100644 --- a/lib/widgets/video_player/widgets/chewie/chewie_player.dart +++ b/lib/widgets/video_player/widgets/chewie/chewie_player.dart @@ -1,12 +1,13 @@ import 'dart:async'; -import 'package:Okuna/widgets/video_player/widgets/chewie/chewie_progress_colors.dart'; import 'package:Okuna/widgets/video_player/widgets/chewie/player_with_controls.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:screen/screen.dart'; import 'package:video_player/video_player.dart'; +import 'package:wakelock/wakelock.dart'; + +import 'chewie_progress_colors.dart'; typedef Widget ChewieRoutePageBuilder( BuildContext context, @@ -69,7 +70,7 @@ class ChewieState extends State { _isFullScreen = true; await _pushFullScreenWidget(context); } else if (_isFullScreen) { - Navigator.of(context).pop(); + Navigator.of(context, rootNavigator: true).pop(); _isFullScreen = false; } } @@ -110,10 +111,10 @@ class ChewieState extends State { } Widget _fullScreenRoutePageBuilder( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - ) { + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { var controllerProvider = _ChewieControllerProvider( controller: widget.controller, child: PlayerWithControls(), @@ -130,7 +131,6 @@ class ChewieState extends State { Future _pushFullScreenWidget(BuildContext context) async { final isAndroid = Theme.of(context).platform == TargetPlatform.android; final TransitionRoute route = PageRouteBuilder( - settings: RouteSettings(isInitialRoute: false), pageBuilder: _fullScreenRoutePageBuilder, ); @@ -143,17 +143,16 @@ class ChewieState extends State { } if (!widget.controller.allowedScreenSleep) { - Screen.keepOn(true); + Wakelock.enable(); } - await Navigator.of(context).push(route); + await Navigator.of(context, rootNavigator: true).push(route); _isFullScreen = false; widget.controller.exitFullScreen(); - bool isKeptOn = await Screen.isKeptOn; - if (isKeptOn) { - Screen.keepOn(false); - } + // The wakelock plugins checks whether it needs to perform an action internally, + // so we do not need to check Wakelock.isEnabled. + Wakelock.disable(); SystemChrome.setEnabledSystemUIOverlays( widget.controller.systemOverlaysAfterFullScreen); @@ -202,7 +201,7 @@ class ChewieController extends ChangeNotifier { ], this.routePageBuilder = null, }) : assert(videoPlayerController != null, - 'You must provide a controller to play a video') { + 'You must provide a controller to play a video') { _initialize(); } @@ -282,8 +281,8 @@ class ChewieController extends ChangeNotifier { static ChewieController of(BuildContext context) { final chewieControllerProvider = - context.inheritFromWidgetOfExactType(_ChewieControllerProvider) - as _ChewieControllerProvider; + context.inheritFromWidgetOfExactType(_ChewieControllerProvider) + as _ChewieControllerProvider; return chewieControllerProvider.controller; } @@ -292,6 +291,8 @@ class ChewieController extends ChangeNotifier { bool get isFullScreen => _isFullScreen; + bool get isPlaying => videoPlayerController.value.isPlaying; + Future _initialize() async { await videoPlayerController.setLooping(looping); @@ -339,6 +340,10 @@ class ChewieController extends ChangeNotifier { notifyListeners(); } + void togglePause() { + isPlaying ? pause() : play(); + } + Future play() async { await videoPlayerController.play(); } @@ -374,4 +379,4 @@ class _ChewieControllerProvider extends InheritedWidget { @override bool updateShouldNotify(_ChewieControllerProvider old) => controller != old.controller; -} +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 7392508c9..677d02bdf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -35,7 +35,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.4.1" back_button_interceptor: dependency: "direct main" description: @@ -49,7 +49,7 @@ packages: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "2.0.0" cached_network_image: dependency: "direct main" description: @@ -63,14 +63,21 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.1.3" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" connectivity: dependency: "direct main" description: @@ -91,7 +98,7 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "0.13.3+1" + version: "0.13.7" crypto: dependency: transitive description: @@ -141,6 +148,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" file_picker: dependency: "direct main" description: @@ -458,7 +472,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.4" + version: "1.7.0" path_drawing: dependency: transitive description: @@ -652,7 +666,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.7.0" sprintf: dependency: "direct main" description: @@ -715,21 +729,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.9.4" + version: "1.13.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.11" + version: "0.2.15" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.2.15" + version: "0.3.1" throttling: dependency: "direct main" description: @@ -844,6 +858,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.15" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.1" xml: dependency: transitive description: @@ -859,5 +880,5 @@ packages: source: hosted version: "2.2.0" sdks: - dart: ">=2.5.0 <3.0.0" + dart: ">=2.6.0 <3.0.0" flutter: ">=1.10.15-pre.148 <2.0.0" From 552ea3f03a0172e58c26e662e02a9e5f4a99b358 Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Sun, 3 May 2020 16:18:46 +0200 Subject: [PATCH 20/39] :package: update packages --- ios/Podfile.lock | 169 +++++++++--- .../home/bottom_sheets/image_picker.dart | 2 +- .../home/bottom_sheets/video_picker.dart | 2 +- pubspec.lock | 241 ++++++++++++------ 4 files changed, 287 insertions(+), 127 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 0634e5d68..a134c186d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2,59 +2,112 @@ PODS: - connectivity (0.0.1): - Flutter - Reachability + - connectivity_macos (0.0.1): + - Flutter - device_info (0.0.1): - Flutter + - DKImagePickerController/Core (4.2.2): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.2.2) + - DKImagePickerController/PhotoGallery (4.2.2): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.2.2) + - DKPhotoGallery (0.0.14): + - DKPhotoGallery/Core (= 0.0.14) + - DKPhotoGallery/Model (= 0.0.14) + - DKPhotoGallery/Preview (= 0.0.14) + - DKPhotoGallery/Resource (= 0.0.14) + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Core (0.0.14): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Model (0.0.14): + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Preview (0.0.14): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Resource (0.0.14): + - SDWebImage + - SDWebImageFLPlugin - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery - Flutter + - FLAnimatedImage (1.0.12) - Flutter (1.0.0) - - flutter_ffmpeg/full-gpl-lts (0.2.7): + - flutter_ffmpeg/full-gpl-lts (0.2.10): - Flutter - - mobile-ffmpeg-full-gpl (= 4.2.2.LTS) + - mobile-ffmpeg-full-gpl (= 4.3.1.LTS) - flutter_image_compress (0.0.1): - Flutter - Mantle + - SDWebImageWebPCoder + - flutter_plugin_android_lifecycle (0.0.1): + - Flutter - flutter_secure_storage (3.3.1): - Flutter - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - image_cropper (0.0.1): + - image_cropper (0.0.2): - Flutter - - TOCropViewController (~> 2.5.1) + - TOCropViewController (~> 2.5.2) - image_picker (0.0.1): - Flutter - - Intercom (5.5.2) - - "intercom_flutter (2.0.5+2)": + - Intercom (6.0.2) + - intercom_flutter (2.1.1): - Flutter - - Intercom (~> 5.5.1) - - libwebp (1.0.3): - - libwebp/demux (= 1.0.3) - - libwebp/mux (= 1.0.3) - - libwebp/webp (= 1.0.3) - - libwebp/demux (1.0.3): + - Intercom (~> 6.0.1) + - libwebp (1.1.0): + - libwebp/demux (= 1.1.0) + - libwebp/mux (= 1.1.0) + - libwebp/webp (= 1.1.0) + - libwebp/demux (1.1.0): - libwebp/webp - - libwebp/mux (1.0.3): + - libwebp/mux (1.1.0): - libwebp/demux - - libwebp/webp (1.0.3) - - Mantle (2.1.0): - - Mantle/extobjc (= 2.1.0) - - Mantle/extobjc (2.1.0) - - mobile-ffmpeg-full-gpl (4.2.2.LTS) - - OneSignal (2.11.2) - - onesignal_flutter (2.2.0): - - Flutter - - OneSignal (= 2.11.2) + - libwebp/webp (1.1.0) + - Mantle (2.1.1): + - Mantle/extobjc (= 2.1.1) + - Mantle/extobjc (2.1.1) + - mobile-ffmpeg-full-gpl (4.3.1.LTS) + - OneSignal (2.13.1) + - onesignal_flutter (2.4.1): + - Flutter + - OneSignal (= 2.13.1) - package_info (0.0.1): - Flutter - path_provider (0.0.1): - Flutter + - path_provider_macos (0.0.1): + - Flutter - Reachability (3.2) - screen (0.0.1): - Flutter - - share (0.5.2): + - SDWebImage (5.7.3): + - SDWebImage/Core (= 5.7.3) + - SDWebImage/Core (5.7.3) + - SDWebImageFLPlugin (0.4.0): + - FLAnimatedImage (>= 1.0.11) + - SDWebImage/Core (~> 5.6) + - SDWebImageWebPCoder (0.6.1): + - libwebp (~> 1.0) + - SDWebImage/Core (~> 5.7) + - share (0.0.1): - Flutter - shared_preferences (0.0.1): - Flutter + - shared_preferences_macos (0.0.1): + - Flutter + - shared_preferences_web (0.0.1): + - Flutter - sqflite (0.0.1): - Flutter - FMDB (~> 2.7.2) @@ -75,11 +128,13 @@ PODS: DEPENDENCIES: - connectivity (from `.symlinks/plugins/connectivity/ios`) + - connectivity_macos (from `.symlinks/plugins/connectivity_macos/ios`) - device_info (from `.symlinks/plugins/device_info/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - flutter_ffmpeg/full-gpl-lts (from `.symlinks/plugins/flutter_ffmpeg/ios`) - flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`) + - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - image_cropper (from `.symlinks/plugins/image_cropper/ios`) - image_picker (from `.symlinks/plugins/image_picker/ios`) @@ -88,9 +143,12 @@ DEPENDENCIES: - onesignal_flutter (from `.symlinks/plugins/onesignal_flutter/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) + - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`) - screen (from `.symlinks/plugins/screen/ios`) - share (from `.symlinks/plugins/share/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`) + - shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - uni_links (from `.symlinks/plugins/uni_links/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`) @@ -100,6 +158,9 @@ DEPENDENCIES: SPEC REPOS: trunk: + - DKImagePickerController + - DKPhotoGallery + - FLAnimatedImage - FMDB - Intercom - libwebp @@ -107,12 +168,17 @@ SPEC REPOS: - mobile-ffmpeg-full-gpl - OneSignal - Reachability + - SDWebImage + - SDWebImageFLPlugin + - SDWebImageWebPCoder - TOCropViewController - VIMediaCache EXTERNAL SOURCES: connectivity: :path: ".symlinks/plugins/connectivity/ios" + connectivity_macos: + :path: ".symlinks/plugins/connectivity_macos/ios" device_info: :path: ".symlinks/plugins/device_info/ios" file_picker: @@ -123,6 +189,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_ffmpeg/ios" flutter_image_compress: :path: ".symlinks/plugins/flutter_image_compress/ios" + flutter_plugin_android_lifecycle: + :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" flutter_secure_storage: :path: ".symlinks/plugins/flutter_secure_storage/ios" image_cropper: @@ -137,12 +205,18 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" + path_provider_macos: + :path: ".symlinks/plugins/path_provider_macos/ios" screen: :path: ".symlinks/plugins/screen/ios" share: :path: ".symlinks/plugins/share/ios" shared_preferences: :path: ".symlinks/plugins/shared_preferences/ios" + shared_preferences_macos: + :path: ".symlinks/plugins/shared_preferences_macos/ios" + shared_preferences_web: + :path: ".symlinks/plugins/shared_preferences_web/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" uni_links: @@ -157,37 +231,48 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/wakelock/ios" SPEC CHECKSUMS: - connectivity: c72716e202a1225ec4810740d5cb56b8ae3bf4cc - device_info: 3ebad48f726348f69abd802f3334a8d1ed795fbd - file_picker: 408623be2125b79a4539cf703be3d4b3abe5e245 + connectivity: 6e94255659cc86dcbef1d452ad3e0491bb1b3e75 + connectivity_macos: e2e9731b6b22dda39eb1b128f6969d574460e191 + device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 + DKImagePickerController: 4a3e7948a848c4348e600b3fe5ce41478835fa10 + DKPhotoGallery: 0290d32343574f06eaa4c26f8f2f8a1035e916be + file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 + FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31 Flutter: 0e3d915762c693b495b44d77113d4970485de6ec - flutter_ffmpeg: 05ee218287a2b55a13df8f7970aab1a52094083e - flutter_image_compress: f69d0e0e078ce52b4810695593bc861ee319ae7d + flutter_ffmpeg: cf0a6941ef67e88248c998cc3e34f8acb0b4e454 + flutter_image_compress: 082f8daaf6c1b0c9fe798251c750ef0ecd98d7ae + flutter_plugin_android_lifecycle: dc0b544e129eebb77a6bfb1239d4d1c673a60a35 flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - image_cropper: 966bf742cd9cee8d78010ec5c67dd9e7826e6b19 + image_cropper: 3c16d7651730ffe85897f5a1c4e2547e6b54989a image_picker: e3eacd46b94694dde7cf2705955cece853aa1a8f - Intercom: 1151a15e5e7931d8e401671a4357239d44325159 - intercom_flutter: bfadb1c7e3d0af65b91cef2a53a29f5a9abc32c1 - libwebp: 057912d6d0abfb6357d8bb05c0ea470301f5d61e - Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b - mobile-ffmpeg-full-gpl: efbd28ed385c931e90421f4f3a78a2349c3c76d0 - OneSignal: 4e3e68bb6c29975286a2fa910cb49132bc6e20bb - onesignal_flutter: 5a6041629e3f93a61cf7ae8193c3d1702f2ef78d - package_info: 78cabb3c322943c55d39676f4a5bfc748c01d055 - path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d + Intercom: 523417d82ed1a8c635cc7f97b266eb8bd0bd9d85 + intercom_flutter: 45b06d70365ccae12227cbceeca36c6a23457f54 + libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 + Mantle: 35238ae6f2e2b2d474fa7b67fee82a59fea71915 + mobile-ffmpeg-full-gpl: b1e8e1540852bd911768774f1c11e8fc76102dc3 + OneSignal: fa98eaa90372bb367b6a4a1c1622ffb9832c7600 + onesignal_flutter: fc5794fdcfdbd3f17426cb63c78281a3f51c85ff + package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 + path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 screen: abd91ca7bf3426e1cc3646d27e9b2358d6bf07b0 - share: 7d22fe8baedfe93aefd864bf0b73f29711fbb0a3 - shared_preferences: 1feebfa37bb57264736e16865e7ffae7fc99b523 - sqflite: ff1d9da63c06588cc8d1faf7256d741f16989d5a + SDWebImage: 97351f6582ceca541ea294ba66a1fcb342a331c2 + SDWebImageFLPlugin: 6c2295fb1242d44467c6c87dc5db6b0a13228fd8 + SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21 + share: 0b2c3e82132f5888bccca3351c504d0003b3b410 + shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d + shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087 + shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9 + sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 TOCropViewController: e9da34f484aedd4e5d5a8ab230ba217cfe16c729 uni_links: d97da20c7701486ba192624d99bffaaffcfc298a url_launcher: 0067ddb8f10d36786672aa0722a21717dba3a298 video_player: fd8b6188941ec5a530d6ab0830a34f78faa0452e video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1 VIMediaCache: aa650f82cb114c68a343beb4e67416cf5eb2f3a2 - wakelock: da5012e206ec8978df58d8642e4cdd4fe506185f + wakelock: 0d4a70faf8950410735e3f61fb15d517c8a6efc4 PODFILE CHECKSUM: 19fc3c8c5b57454c416c7e16986dc7dd8aa66d48 diff --git a/lib/pages/home/bottom_sheets/image_picker.dart b/lib/pages/home/bottom_sheets/image_picker.dart index 24c3d8973..9110abf01 100644 --- a/lib/pages/home/bottom_sheets/image_picker.dart +++ b/lib/pages/home/bottom_sheets/image_picker.dart @@ -28,7 +28,7 @@ class OBImagePickerBottomSheet extends StatelessWidget { bool permissionGranted = await provider.permissionService .requestStoragePermissions(context: context); if (permissionGranted) { - File file = await FilePicker.getFile(type: FileType.IMAGE); + File file = await FilePicker.getFile(type: FileType.image); Navigator.pop(context, file); } }, diff --git a/lib/pages/home/bottom_sheets/video_picker.dart b/lib/pages/home/bottom_sheets/video_picker.dart index d16911074..1784617a3 100644 --- a/lib/pages/home/bottom_sheets/video_picker.dart +++ b/lib/pages/home/bottom_sheets/video_picker.dart @@ -27,7 +27,7 @@ class OBVideoPickerBottomSheet extends StatelessWidget { bool permissionGranted = await provider.permissionService .requestStoragePermissions(context: context); if (permissionGranted) { - File file = await FilePicker.getFile(type: FileType.VIDEO); + File file = await FilePicker.getFile(type: FileType.video); Navigator.pop(context, file); } }, diff --git a/pubspec.lock b/pubspec.lock index 677d02bdf..b4c2f3ed3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" after_layout: dependency: transitive description: @@ -14,21 +21,21 @@ packages: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.36.4" + version: "0.39.8" archive: dependency: transitive description: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.13" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.5.2" + version: "1.6.0" async: dependency: transitive description: @@ -42,7 +49,7 @@ packages: name: back_button_interceptor url: "https://pub.dartlang.org" source: hosted - version: "4.1.1" + version: "4.2.3" boolean_selector: dependency: transitive description: @@ -56,7 +63,7 @@ packages: name: cached_network_image url: "https://pub.dartlang.org" source: hosted - version: "2.0.0-rc" + version: "2.0.0" charcode: dependency: transitive description: @@ -84,7 +91,21 @@ packages: name: connectivity url: "https://pub.dartlang.org" source: hosted - version: "0.4.4" + version: "0.4.8+2" + connectivity_macos: + dependency: transitive + description: + name: connectivity_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0+2" + connectivity_platform_interface: + dependency: transitive + description: + name: connectivity_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" convert: dependency: transitive description: @@ -98,14 +119,14 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "0.13.7" + version: "0.13.9" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.4" csslib: dependency: transitive description: @@ -119,14 +140,14 @@ packages: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.1.3" dart_style: dependency: transitive description: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "1.2.9" + version: "1.3.6" dcache: dependency: "direct main" description: @@ -140,7 +161,7 @@ packages: name: device_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.0+3" + version: "0.4.2+2" expandable: dependency: "direct main" description: @@ -161,7 +182,7 @@ packages: name: file_picker url: "https://pub.dartlang.org" source: hosted - version: "1.4.2" + version: "1.9.0" flutter: dependency: "direct main" description: flutter @@ -180,35 +201,35 @@ packages: name: flutter_cache_manager url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.1.3" flutter_colorpicker: dependency: "direct main" description: name: flutter_colorpicker url: "https://pub.dartlang.org" source: hosted - version: "0.2.6" + version: "0.3.4" flutter_ffmpeg: dependency: "direct main" description: name: flutter_ffmpeg url: "https://pub.dartlang.org" source: hosted - version: "0.2.7" + version: "0.2.10" flutter_image_compress: dependency: "direct main" description: name: flutter_image_compress url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.7" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.7.3" + version: "0.7.5" flutter_localizations: dependency: "direct main" description: flutter @@ -228,13 +249,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.3" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.7" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage url: "https://pub.dartlang.org" source: hosted - version: "3.3.1+1" + version: "3.3.3" flutter_slidable: dependency: "direct main" description: @@ -248,33 +276,31 @@ packages: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "0.14.2" + version: "0.14.4" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_widgets: dependency: "direct main" description: name: flutter_widgets url: "https://pub.dartlang.org" source: hosted - version: "0.1.6" - front_end: - dependency: transitive - description: - name: front_end - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.19" + version: "0.1.12" glob: dependency: transitive description: name: glob url: "https://pub.dartlang.org" source: hosted - version: "1.1.7" + version: "1.2.0" html: dependency: "direct main" description: @@ -288,21 +314,21 @@ packages: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.0+2" + version: "0.12.1" http_multi_server: dependency: transitive description: name: http_multi_server url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.0" http_parser: dependency: transitive description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "3.1.3" + version: "3.1.4" http_retry: dependency: "direct main" description: @@ -316,14 +342,14 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.12" image_cropper: dependency: "direct main" description: name: image_cropper url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.1" image_picker: dependency: "direct main" description: @@ -337,7 +363,7 @@ packages: name: intercom_flutter url: "https://pub.dartlang.org" source: hosted - version: "2.0.5+2" + version: "2.2.0" intl: dependency: "direct main" description: @@ -351,7 +377,7 @@ packages: name: intl_translation url: "https://pub.dartlang.org" source: hosted - version: "0.17.7" + version: "0.17.10" inview_notifier_list: dependency: "direct main" description: @@ -367,7 +393,7 @@ packages: name: io url: "https://pub.dartlang.org" source: hosted - version: "0.3.3" + version: "0.3.4" js: dependency: transitive description: @@ -375,27 +401,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.1+1" - kernel: - dependency: transitive - description: - name: kernel - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.19" logging: dependency: transitive description: name: logging url: "https://pub.dartlang.org" source: hosted - version: "0.11.3+2" + version: "0.11.4" markdown: dependency: transitive description: name: markdown url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.3" matcher: dependency: transitive description: @@ -424,6 +443,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + node_interop: + dependency: transitive + description: + name: node_interop + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + node_io: + dependency: transitive + description: + name: node_io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1+2" node_preamble: dependency: transitive description: @@ -437,7 +470,7 @@ packages: name: onesignal_flutter url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.4.1" open_iconic_flutter: dependency: "direct main" description: @@ -451,21 +484,14 @@ packages: name: package_config url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.9.3" package_info: dependency: "direct main" description: name: package_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.0+6" - package_resolver: - dependency: transitive - description: - name: package_resolver - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.10" + version: "0.4.0+17" path: dependency: "direct main" description: @@ -493,21 +519,35 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.4.0" + version: "1.6.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.8.0+1" + version: "1.9.0" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "3.0.2" photo_view: dependency: "direct main" description: @@ -529,6 +569,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" pool: dependency: transitive description: @@ -542,7 +589,7 @@ packages: name: pub_semver url: "https://pub.dartlang.org" source: hosted - version: "1.4.2" + version: "1.4.4" public_suffix: dependency: "direct main" description: @@ -563,7 +610,7 @@ packages: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.1.3" retry: dependency: "direct main" description: @@ -591,21 +638,42 @@ packages: name: sentry url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.3.1" share: dependency: "direct main" description: name: share url: "https://pub.dartlang.org" source: hosted - version: "0.6.2+3" + version: "0.6.4" shared_preferences: dependency: "direct main" description: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "0.5.3+4" + version: "0.5.7" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+7" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2+4" shelf: dependency: transitive description: @@ -619,7 +687,7 @@ packages: name: shelf_packages_handler url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.0" shelf_static: dependency: transitive description: @@ -640,7 +708,7 @@ packages: name: shimmer url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.1.1" sky_engine: dependency: transitive description: flutter @@ -652,14 +720,14 @@ packages: name: source_map_stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.1.5" + version: "2.0.0" source_maps: dependency: transitive description: name: source_maps url: "https://pub.dartlang.org" source: hosted - version: "0.10.8" + version: "0.10.9" source_span: dependency: transitive description: @@ -680,7 +748,14 @@ packages: name: sqflite url: "https://pub.dartlang.org" source: hosted - version: "1.1.7" + version: "1.3.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0+1" stack_trace: dependency: transitive description: @@ -701,7 +776,7 @@ packages: name: stream_transform url: "https://pub.dartlang.org" source: hosted - version: "0.0.19" + version: "0.0.20" string_scanner: dependency: transitive description: @@ -715,7 +790,7 @@ packages: name: synchronized url: "https://pub.dartlang.org" source: hosted - version: "2.1.0+1" + version: "2.2.0" term_glyph: dependency: transitive description: @@ -729,7 +804,7 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.13.0" + version: "1.14.3" test_api: dependency: transitive description: @@ -743,7 +818,7 @@ packages: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.3.1" + version: "0.3.4" throttling: dependency: "direct main" description: @@ -757,7 +832,7 @@ packages: name: timeago url: "https://pub.dartlang.org" source: hosted - version: "2.0.20" + version: "2.0.26" tinycolor: dependency: "direct main" description: @@ -799,7 +874,7 @@ packages: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.4" validators: dependency: "direct main" description: @@ -829,35 +904,35 @@ packages: name: video_thumbnail url: "https://pub.dartlang.org" source: hosted - version: "0.1.6" + version: "0.1.7" vm_service: dependency: transitive description: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "2.1.2" + version: "4.0.2" wakelock: dependency: "direct main" description: name: wakelock url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4+1" watcher: dependency: transitive description: name: watcher url: "https://pub.dartlang.org" source: hosted - version: "0.9.7+12" + version: "0.9.7+15" web_socket_channel: dependency: transitive description: name: web_socket_channel url: "https://pub.dartlang.org" source: hosted - version: "1.0.15" + version: "1.1.0" webkit_inspection_protocol: dependency: transitive description: @@ -871,14 +946,14 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.5.0" + version: "3.7.0" yaml: dependency: transitive description: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.2.1" sdks: - dart: ">=2.6.0 <3.0.0" - flutter: ">=1.10.15-pre.148 <2.0.0" + dart: ">=2.7.0 <3.0.0" + flutter: ">=1.12.13+hotfix.7 <2.0.0" From c2e6476cf3076999a640432051dda381c9ccc393 Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Mon, 4 May 2020 14:33:06 +0200 Subject: [PATCH 21/39] :bug: update packages, fix more breaking changes --- .gitignore | 3 +- ios/Podfile.lock | 2 +- ios/Runner.xcodeproj/project.pbxproj | 136 +++++++++++++++--- .../xcshareddata/WorkspaceSettings.xcsettings | 8 -- pubspec.lock | 22 +-- pubspec.yaml | 12 +- 6 files changed, 139 insertions(+), 44 deletions(-) delete mode 100755 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/.gitignore b/.gitignore index 6d0a9f2fe..f7243428c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ report.xml /crowdin.yml -.flutter-plugins-dependencies \ No newline at end of file +.flutter-plugins-dependencies +/ios/Flutter/.last_build_id diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a134c186d..26caa5b21 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -276,4 +276,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 19fc3c8c5b57454c416c7e16986dc7dd8aa66d48 -COCOAPODS: 1.8.3 +COCOAPODS: 1.9.1 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index b959651cd..e924c6bd3 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -9,13 +9,9 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 8959D90F23F955D80091EB2B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 898053AA22047AAF00E47AD9 /* Pods_Runner.framework */; }; + 89398C16245F1D5F00F7C8DE /* Pods_Runner.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 898053AA22047AAF00E47AD9 /* Pods_Runner.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 89ABAE522203425900049DFB /* NotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 89ABAE512203425900049DFB /* NotificationService.m */; }; 89ABAE562203425900049DFB /* OneSignalNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 89ABAE4E2203425900049DFB /* OneSignalNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -76,6 +72,55 @@ remoteGlobalIDString = 603FA97A9EAECDA6694299BF06D80C42; remoteInfo = sqflite; }; + 89398C07245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = E95F4135A781F1B0F90A544CD0398420; + remoteInfo = DKImagePickerController; + }; + 89398C09245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 7247E064ECCE473BC39CE9C6B1E7C0B3; + remoteInfo = "DKImagePickerController-DKImagePickerController"; + }; + 89398C0B245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B1B247DE42D782218EFEA0CB0F6D6454; + remoteInfo = DKPhotoGallery; + }; + 89398C0D245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B85D4E7E4EAEF1DAD4C541A2D2E6F27D; + remoteInfo = "DKPhotoGallery-DKPhotoGallery"; + }; + 89398C0F245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = FAA5F2D71B90788C908800A94534AA92; + remoteInfo = FLAnimatedImage; + }; + 89398C11245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 84D8D6D3A0E58CD8A5EEB311D34E6BDB; + remoteInfo = flutter_plugin_android_lifecycle; + }; + 89398C13245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 7FA68B460F5DC5F5BDB312C44D7C1963; + remoteInfo = SDWebImageFLPlugin; + }; 8958C896234B7D5E000280FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; @@ -261,6 +306,17 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 89398C17245F1D6000F7C8DE /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 89398C16245F1D5F00F7C8DE /* Pods_Runner.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 89ABAE5F2203425900049DFB /* Embed App Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -278,8 +334,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -297,7 +351,6 @@ 37BDAB2A2170B9A900811BA5 /* development.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = development.plist; sourceTree = ""; }; 37BDAB2B2170B9AA00811BA5 /* production.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = production.plist; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 418B7159EB81FFD151CA68C6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 75F711C5FC14A5361C4912AE /* Pods-OneSignalNotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.release.xcconfig"; sourceTree = ""; }; 77585F4F710D7AE9AB748E2F /* Pods-OneSignalNotificationServiceExtension.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.release-production.xcconfig"; path = "Pods/Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.release-production.xcconfig"; sourceTree = ""; }; @@ -321,7 +374,6 @@ 89DBC2DD2203430700F80685 /* OneSignalNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OneSignalNotificationServiceExtension.entitlements; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -346,7 +398,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8959D90F23F955D80091EB2B /* Pods_Runner.framework in Frameworks */, B28D3FB500E916EAB5DDC3CB /* Pods_OneSignalNotificationServiceExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -355,8 +406,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, D4D1718378280D051194AC59 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -414,9 +463,15 @@ children = ( 89FE1C6B23268E35007C6904 /* connectivity.framework */, 8925094A222F0E0900455D87 /* device_info.framework */, + 89398C08245F1D5F00F7C8DE /* DKImagePickerController.framework */, + 89398C0A245F1D5F00F7C8DE /* DKImagePickerController.bundle */, + 89398C0C245F1D5F00F7C8DE /* DKPhotoGallery.framework */, + 89398C0E245F1D5F00F7C8DE /* DKPhotoGallery.bundle */, 89E684862320235F00E0855B /* file_picker.framework */, + 89398C10245F1D5F00F7C8DE /* FLAnimatedImage.framework */, 89E684882320235F00E0855B /* flutter_ffmpeg.framework */, 89FE1BA52323BCA0007C6904 /* flutter_image_compress.framework */, + 89398C12245F1D5F00F7C8DE /* flutter_plugin_android_lifecycle.framework */, 8980539E22047AAF00E47AD9 /* flutter_secure_storage.framework */, 8928E31E22356450001DB32A /* FMDB.framework */, 898053A022047AAF00E47AD9 /* image_cropper.framework */, @@ -432,6 +487,7 @@ 89FE1C6D23268E35007C6904 /* Reachability.framework */, 89E6848C2320235F00E0855B /* screen.framework */, 8981C97823F82B3200DE0290 /* SDWebImage.framework */, + 89398C14245F1D5F00F7C8DE /* SDWebImageFLPlugin.framework */, 8981C97A23F82B3200DE0290 /* SDWebImageWebPCoder.framework */, 89EE3231227B56510094ACB0 /* share.framework */, 89EE3233227B56510094ACB0 /* shared_preferences.framework */, @@ -462,9 +518,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -535,6 +589,7 @@ 89ABAE4A2203425900049DFB /* Sources */, 89ABAE4B2203425900049DFB /* Frameworks */, 89ABAE4C2203425900049DFB /* Resources */, + 89398C17245F1D6000F7C8DE /* Embed Frameworks */, ); buildRules = ( ); @@ -691,6 +746,55 @@ remoteRef = 8928E31F22356450001DB32A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 89398C08245F1D5F00F7C8DE /* DKImagePickerController.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = DKImagePickerController.framework; + remoteRef = 89398C07245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C0A245F1D5F00F7C8DE /* DKImagePickerController.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = DKImagePickerController.bundle; + remoteRef = 89398C09245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C0C245F1D5F00F7C8DE /* DKPhotoGallery.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = DKPhotoGallery.framework; + remoteRef = 89398C0B245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C0E245F1D5F00F7C8DE /* DKPhotoGallery.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = DKPhotoGallery.bundle; + remoteRef = 89398C0D245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C10245F1D5F00F7C8DE /* FLAnimatedImage.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FLAnimatedImage.framework; + remoteRef = 89398C0F245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C12245F1D5F00F7C8DE /* flutter_plugin_android_lifecycle.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = flutter_plugin_android_lifecycle.framework; + remoteRef = 89398C11245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C14245F1D5F00F7C8DE /* SDWebImageFLPlugin.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = SDWebImageFLPlugin.framework; + remoteRef = 89398C13245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 8958C897234B7D5E000280FF /* package_info.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -932,7 +1036,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 8614A00078062C58F9149142 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -1722,7 +1826,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -1779,7 +1882,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100755 index 949b67898..000000000 --- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - BuildSystemType - Original - - diff --git a/pubspec.lock b/pubspec.lock index b4c2f3ed3..d67fba2cf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -8,13 +8,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0" - after_layout: - dependency: transitive - description: - name: after_layout - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.7+2" analyzer: dependency: transitive description: @@ -162,6 +155,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.2+2" + exif: + dependency: "direct main" + description: + name: exif + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" expandable: dependency: "direct main" description: @@ -194,7 +194,7 @@ packages: name: flutter_advanced_networkimage url: "https://pub.dartlang.org" source: hosted - version: "0.6.0-alpha.1" + version: "0.7.0" flutter_cache_manager: dependency: "direct main" description: @@ -276,7 +276,7 @@ packages: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "0.14.4" + version: "0.17.4" flutter_test: dependency: "direct dev" description: flutter @@ -356,7 +356,7 @@ packages: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.6.1+10" + version: "0.6.3+1" intercom_flutter: dependency: "direct main" description: @@ -554,7 +554,7 @@ packages: name: photo_view url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.9.2" pigment: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index f960d93e9..d6ac28062 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,7 +31,7 @@ dependencies: retry: ^3.0.0+1 shared_preferences: ^0.5.2 flutter_markdown: ^0.2.0 - file_picker: ^1.4.2 + file_picker: ^1.9.0 sentry: ^2.2.0 screen: ^0.0.5 back_button_interceptor: ^4.0.1 @@ -41,7 +41,7 @@ dependencies: flutter_pagewise: ^1.2.2 tinycolor: ^1.0.2 onesignal_flutter: ^2.2.0 - flutter_advanced_networkimage: 0.6.0-alpha.1 + flutter_advanced_networkimage: ^0.7.0 dcache: ^0.1.0 validators: ^1.0.0+1 url_launcher: ^4.0.1 @@ -52,8 +52,8 @@ dependencies: timeago: ^2.0.9 public_suffix: ^1.2.0 pigment: ^1.0.3 - photo_view: ^0.4.2 - flutter_svg: ^0.14.0 + photo_view: ^0.9.2 + flutter_svg: ^0.17.4 flutter_secure_storage: ^3.1.2 mime: ^0.9.6+2 http: ^0.12.0 @@ -70,8 +70,8 @@ dependencies: image_picker: 0.6.3+1 image_cropper: ^1.2.1 shimmer: ^1.0.0 - share: ^0.6.1 - path: ^1.6.2 + share: ^0.6.4 + path: ^1.7.0 package_info: ^0.4.0 flutter: sdk: flutter From 5aa85f447bf9478c5cf6fae6d8061873639c5e0c Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Mon, 4 May 2020 17:34:54 +0200 Subject: [PATCH 22/39] :bug: update slack url --- lib/pages/home/pages/menu/pages/useful_links.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/home/pages/menu/pages/useful_links.dart b/lib/pages/home/pages/menu/pages/useful_links.dart index 5ef113b5d..0c49b553c 100644 --- a/lib/pages/home/pages/menu/pages/useful_links.dart +++ b/lib/pages/home/pages/menu/pages/useful_links.dart @@ -95,7 +95,7 @@ class OBUsefulLinksPage extends StatelessWidget { _localizationService.drawer__useful_links_slack_channel_desc), onTap: () { urlLauncherService.launchUrl( - 'https://join.slack.com/t/okuna/shared_invite/enQtNDI2NjI3MDM0MzA2LTYwM2E1Y2NhYWRmNTMzZjFhYWZlYmM2YTQ0MWEwYjYyMzcxMGI0MTFhNTIwYjU2ZDI1YjllYzlhOWZjZDc4ZWY'); + 'https://join.slack.com/t/okuna/shared_invite/zt-5fzmpygy-V5nbMzmNJnEg5Hiwx4LO~w'); }, ), ListTile( From 43a4e67ac8c644b28f715a93de1eb78ee68179d4 Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Mon, 4 May 2020 16:33:22 +0200 Subject: [PATCH 23/39] :package: update uni links package --- pubspec.lock | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index d67fba2cf..6e60e02d4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -853,7 +853,7 @@ packages: name: uni_links url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.2.0" url_launcher: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d6ac28062..391356dd0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,7 +45,7 @@ dependencies: dcache: ^0.1.0 validators: ^1.0.0+1 url_launcher: ^4.0.1 - uni_links: ^0.1.4 + uni_links: ^0.2.0 flutter_slidable: "^0.4.9" flutter_cache_manager: ^1.1.1 cached_network_image: ^2.0.0-rc From 610ba91cb64f8403042d605c3cb1827fca51483b Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Tue, 5 May 2020 00:10:10 +0200 Subject: [PATCH 24/39] :bug: fix gif picker does not work --- lib/main.dart | 96 ++++++++++------------------------------- lib/services/media.dart | 8 ++-- 2 files changed, 25 insertions(+), 79 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index e73b9ff42..a4537adc1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -29,8 +29,6 @@ import 'package:Okuna/services/universal_links/universal_links.dart'; import 'package:Okuna/widgets/toast.dart'; import 'package:Okuna/translation/constants.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/foundation.dart' - show debugDefaultTargetPlatformOverride; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:flutter\_localizations/flutter\_localizations.dart'; import 'package:sentry/sentry.dart'; @@ -252,82 +250,32 @@ class _MyAppState extends State { } } -void _setPlatformOverrideForDesktop() { - TargetPlatform targetPlatform; - if (Platform.isMacOS) { - targetPlatform = TargetPlatform.iOS; - } else if (Platform.isLinux || Platform.isWindows) { - targetPlatform = TargetPlatform.android; - } - if (targetPlatform != null) { - debugDefaultTargetPlatformOverride = targetPlatform; - } -} - Future main() async { - _setPlatformOverrideForDesktop(); - // This captures errors reported by the Flutter framework. - FlutterError.onError = (FlutterErrorDetails details) async { - if (isOnDesktop) { - // Report errors on Desktop to embedder - DesktopErrorReporting.reportError(details.exception, details.stack); - } else if (isInDebugMode) { - // In development mode simply print to console. - FlutterError.dumpErrorToConsole(details); - } else { - // In production mode report to the application zone to report to - // Sentry. - Zone.current.handleUncaughtError(details.exception, details.stack); - } - }; - - // This creates a [Zone] that contains the Flutter application and stablishes - // an error handler that captures errors and reports them. - // - // Using a zone makes sure that as many errors as possible are captured, - // including those thrown from [Timer]s, microtasks, I/O, and those forwarded - // from the `FlutterError` handler. - // - // More about zones: - // - // - https://api.dartlang.org/stable/1.24.2/dart-async/Zone-class.html - // - https://www.dartlang.org/articles/libraries/zones - - MyApp app; - // run the entire app with our own HttpieOverride - HttpOverrides.runWithHttpOverrides>( - () async { - app = MyApp(); - runApp(app); - }, - HttpieOverrides()); -} + MyApp app = MyApp(); -/// Reports [error] along with its [stackTrace] to Sentry.io. -Future _reportError( - dynamic error, dynamic stackTrace, SentryClient sentryClient) async { - print('Caught error: $error'); +// Run the whole app in a zone to capture all uncaught errors. + runZonedGuarded(() => runApp(app), (Object error, StackTrace stackTrace) { + if (isInDebugMode) { + print(error); + print(stackTrace); + print('In dev mode. Not sending report to Sentry.io.'); + return; + } - // Errors thrown in development mode are unlikely to be interesting. You can - // check if you are running in dev mode using an assertion and omit sending - // the report. - if (isInDebugMode) { - print(stackTrace); - print('In dev mode. Not sending report to Sentry.io.'); - return; - } + SentryClient sentryClient = + app.openbookProviderKey.currentState.sentryClient; - print('Reporting to Sentry.io...'); - final SentryResponse response = await sentryClient.captureException( - exception: error, - stackTrace: stackTrace, - ); - - if (response.isSuccessful) { - print('Success! Event ID: ${response.eventId}'); - } else { - print('Failed to report to Sentry.io: ${response.error}'); - } + try { + sentryClient.captureException( + exception: error, + stackTrace: stackTrace, + ); + print('Error sent to sentry.io: $error'); + } catch (e) { + print('Sending report to sentry.io failed: $e'); + print('Original error: $error'); + } + }); } bool get isInDebugMode { diff --git a/lib/services/media.dart b/lib/services/media.dart index 4f45e9d03..f35eb01c3 100644 --- a/lib/services/media.dart +++ b/lib/services/media.dart @@ -60,7 +60,6 @@ class MediaService { if (pickedImage == null) return null; - pickedImage = await fixExifRotation(pickedImage); final tempPath = await _getTempPath(); final String processedImageUuid = _uuid.v4(); @@ -75,6 +74,7 @@ class MediaService { bool pickedImageIsGif = await isGif(pickedImage); if (!pickedImageIsGif || flattenGifs) { + pickedImage = await fixExifRotation(pickedImage, deleteOriginal: true); String processedImageName = processedImageUuid + '.jpg'; processedPickedImage = File('$tempPath/$processedImageName'); List convertedImageData = @@ -148,13 +148,11 @@ class MediaService { await fixedImage.writeAsBytes(result); - if(deleteOriginal) await image.delete(); + if (deleteOriginal) await image.delete(); return fixedImage; } - - Future copyMediaFile(File mediaFile, {deleteOriginal: true}) async { final String processedImageUuid = _uuid.v4(); String imageExtension = basename(mediaFile.path); @@ -264,7 +262,7 @@ class MediaService { var exitCode; if (!doCancel()) { exitCode = await flutterFFmpeg.execute( - '-f gif -i $sourceFilePath -pix_fmt yuv420p -c:v libx264 -movflags +faststart -filter:v crop=\'floor(in_w/2)*2:floor(in_h/2)*2\' $resultFilePath'); + ' -loglevel debug -f gif -i $sourceFilePath -pix_fmt yuv420p -c:v libx264 -movflags +faststart -filter:v crop=\'floor(in_w/2)*2:floor(in_h/2)*2\' $resultFilePath'); } if (exitCode == 0) { From 609afea5eeee74d16118365a0d454df068f125f9 Mon Sep 17 00:00:00 2001 From: Komposten Date: Tue, 5 May 2020 17:13:47 +0200 Subject: [PATCH 25/39] :recycle: Rewrite ShareService to handle cancellation better --- lib/services/share.dart | 72 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/lib/services/share.dart b/lib/services/share.dart index 29f0cca11..9fdc9dab8 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -25,12 +25,13 @@ class ShareService { Share _queuedShare; bool _isProcessingShare = false; - CancelableOperation _activeShareOperation; + Map _activeShares; BuildContext _context; ShareService() { _subscribers = []; + _activeShares = {}; if (Platform.isAndroid) { if (_shareReceiveSubscription == null) { @@ -94,11 +95,18 @@ class ShareService { Future _processQueuedShare() async { if (_queuedShare != null) { + // Schedule cancellation of existing share operations. We don't cancel + // immediately since that can cause concurrent modification of _activeShares. + _activeShares + .forEach((key, value) => Future.delayed(Duration(), value.cancel)); + var share = _queuedShare; _queuedShare = null; _isProcessingShare = true; - await _onShare(share); + var operation = CancelableOperation.fromFuture(_onShare(share)); + _activeShares[share] = ShareOperation(share, operation); + _activeShares[share].then(() => _activeShares.remove(share)); _isProcessingShare = false; // Recurse since a new share might have came in while the last was being processed. @@ -111,11 +119,6 @@ class ShareService { File image; File video; - if (_activeShareOperation != null) { - _activeShareOperation.cancel(); - _activeShareOperation = null; - } - if (share.error != null) { _toastService.error( message: _localizationService.trans(share.error), context: _context); @@ -158,12 +161,16 @@ class ShareService { } for (var sub in _subscribers.reversed) { + if (_activeShares[share].isCancelled) { + break; + } + var subResult = await sub(text: text, image: image, video: video); // Stop event propagation if we have a sub-result that is either true or // a CancelableOperation. if (subResult is CancelableOperation) { - _activeShareOperation = subResult; + _activeShares[share].setSubOperation(subResult); break; } else if (subResult == true) { break; @@ -178,3 +185,52 @@ class ShareService { context: _context); } } + +class ShareOperation { + Share share; + CancelableOperation shareOperation; + CancelableOperation subOperation; + bool isCancelled = false; + + bool _shareComplete = false; + bool _subComplete = false; + FutureOr Function() _callback; + + ShareOperation(this.share, this.shareOperation) { + shareOperation.then((_) { + _shareComplete = true; + _complete(); + }); + } + + void setSubOperation(CancelableOperation operation) { + subOperation = operation; + subOperation.then((_) { + _subComplete = true; + _complete(); + }); + + shareOperation.then((_) { + if (shareOperation.isCanceled) { + subOperation.cancel(); + } + }); + } + + void cancel() { + isCancelled = true; + shareOperation?.cancel(); + subOperation?.cancel(); + } + + void then(FutureOr Function() callback) { + _callback = callback; + } + + void _complete() { + if ((subOperation == null || _subComplete) && + (shareOperation == null || _shareComplete)) { + _callback(); + } + } +} From 5210dc24ef13ba663a25d7b16617a8a60f947101 Mon Sep 17 00:00:00 2001 From: Komposten Date: Tue, 5 May 2020 17:27:12 +0200 Subject: [PATCH 26/39] :recycle: Move share GIF conversion from OBSavePostModal to ShareService Also fixes not being able to share GIFs to Okuna if the save post modal is not open. --- .../home/modals/save_post/create_post.dart | 19 +------------------ lib/services/share.dart | 13 +++++++++++++ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/lib/pages/home/modals/save_post/create_post.dart b/lib/pages/home/modals/save_post/create_post.dart index 6bb0efe51..19947f47e 100644 --- a/lib/pages/home/modals/save_post/create_post.dart +++ b/lib/pages/home/modals/save_post/create_post.dart @@ -557,24 +557,7 @@ class OBSavePostModalState extends OBContextualSearchBoxState { } if (video != null) { - final isGif = await _mediaService.isGif(video); - if (isGif) { - var conversionOp = _mediaService.convertGifToVideo(video); - - conversionOp.then((value) { - if (!conversionOp.isCanceled && value != null) { - _setPostVideoFile(value); - } - }, onError: (error, trace) { - if (!conversionOp.isCanceled) { - print(error); - } - }); - - return conversionOp; - } else { - _setPostVideoFile(video); - } + _setPostVideoFile(video); } return true; diff --git a/lib/services/share.dart b/lib/services/share.dart index 9fdc9dab8..6bdbf9ee4 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -147,6 +147,19 @@ class ShareService { _showFileTooLargeToast(_validationService.getAllowedVideoSize()); return; } + + if (await _mediaService.isGif(video)) { + Completer completer = Completer(); + _mediaService + .convertGifToVideo(video) + .then((file) => completer.complete(file), onError: (error, trace) { + print(error); + _toastService.error( + message: _localizationService.error__unknown_error, + context: _context); + }); + video = await completer.future; + } } if (share.text != null) { From bb1f2f12be59a52da28a560f93d431df230a280e Mon Sep 17 00:00:00 2001 From: Komposten Date: Tue, 5 May 2020 19:44:10 +0200 Subject: [PATCH 27/39] :sparkles: Merge Photo and Video buttons, and add a new Camera button Also adds a new pickMedia method to the MediaService which handles e.g. GIF conversion --- .../home/bottom_sheets/camera_picker.dart | 62 +++++++ .../home/bottom_sheets/media_picker.dart | 62 +++++++ .../home/modals/save_post/create_post.dart | 36 ++-- lib/services/bottom_sheet.dart | 28 ++- lib/services/localization.dart | 8 + lib/services/media.dart | 169 ++++++++++++------ lib/widgets/icon.dart | 3 + 7 files changed, 294 insertions(+), 74 deletions(-) create mode 100644 lib/pages/home/bottom_sheets/camera_picker.dart create mode 100644 lib/pages/home/bottom_sheets/media_picker.dart diff --git a/lib/pages/home/bottom_sheets/camera_picker.dart b/lib/pages/home/bottom_sheets/camera_picker.dart new file mode 100644 index 000000000..b515e5762 --- /dev/null +++ b/lib/pages/home/bottom_sheets/camera_picker.dart @@ -0,0 +1,62 @@ +import 'dart:io'; +import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; +import 'package:Okuna/provider.dart'; +import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/media.dart'; +import 'package:Okuna/widgets/icon.dart'; +import 'package:Okuna/widgets/theming/text.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; + +class OBCameraPickerBottomSheet extends StatelessWidget { + const OBCameraPickerBottomSheet({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + var provider = OpenbookProvider.of(context); + + LocalizationService localizationService = provider.localizationService; + + List cameraPickerActions = [ + ListTile( + leading: const OBIcon(OBIcons.camera), + title: OBText( + localizationService.post__create_photo, + ), + onTap: () async { + bool permissionGranted = await provider.permissionService + .requestStoragePermissions(context: context); + if (permissionGranted) { + File file = await ImagePicker.pickImage(source: ImageSource.camera); + Navigator.pop( + context, file != null ? Media(file, FileType.image) : null); + } + }, + ), + ListTile( + leading: const OBIcon(OBIcons.video_camera), + title: OBText( + localizationService.post__create_video, + ), + onTap: () async { + bool permissionGranted = await provider.permissionService + .requestStoragePermissions(context: context); + if (permissionGranted) { + File file = await ImagePicker.pickVideo(source: ImageSource.camera); + Navigator.pop( + context, file != null ? Media(file, FileType.video) : null); + } + }, + ), + ]; + + return OBRoundedBottomSheet( + child: Padding( + padding: EdgeInsets.only(bottom: 16), + child: Column( + children: cameraPickerActions, + mainAxisSize: MainAxisSize.min, + ), + )); + } +} diff --git a/lib/pages/home/bottom_sheets/media_picker.dart b/lib/pages/home/bottom_sheets/media_picker.dart new file mode 100644 index 000000000..8bebd0d4b --- /dev/null +++ b/lib/pages/home/bottom_sheets/media_picker.dart @@ -0,0 +1,62 @@ +import 'dart:io'; +import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; +import 'package:Okuna/provider.dart'; +import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/media.dart'; +import 'package:Okuna/widgets/icon.dart'; +import 'package:Okuna/widgets/theming/text.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; + +class OBMediaPickerBottomSheet extends StatelessWidget { + const OBMediaPickerBottomSheet({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + var provider = OpenbookProvider.of(context); + + LocalizationService localizationService = provider.localizationService; + + List mediaPickerActions = [ + ListTile( + leading: const OBIcon(OBIcons.picture), + title: OBText( + localizationService.post__create_photo, + ), + onTap: () async { + bool permissionGranted = await provider.permissionService + .requestStoragePermissions(context: context); + if (permissionGranted) { + File file = await FilePicker.getFile(type: FileType.image); + Navigator.pop( + context, file != null ? Media(file, FileType.image) : null); + } + }, + ), + ListTile( + leading: const OBIcon(OBIcons.movie), + title: OBText( + localizationService.post__create_video, + ), + onTap: () async { + bool permissionGranted = await provider.permissionService + .requestStoragePermissions(context: context); + if (permissionGranted) { + File file = await FilePicker.getFile(type: FileType.video); + Navigator.pop( + context, file != null ? Media(file, FileType.video) : null); + } + }, + ), + ]; + + return OBRoundedBottomSheet( + child: Padding( + padding: EdgeInsets.only(bottom: 16), + child: Column( + children: mediaPickerActions, + mainAxisSize: MainAxisSize.min, + ), + )); + } +} diff --git a/lib/pages/home/modals/save_post/create_post.dart b/lib/pages/home/modals/save_post/create_post.dart index 6bb0efe51..00a92d070 100644 --- a/lib/pages/home/modals/save_post/create_post.dart +++ b/lib/pages/home/modals/save_post/create_post.dart @@ -35,6 +35,7 @@ import 'package:Okuna/widgets/theming/primary_color_container.dart'; import 'package:Okuna/widgets/theming/smart_text.dart'; import 'package:Okuna/widgets/theming/text.dart'; import 'package:async/async.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:pigment/pigment.dart'; @@ -401,24 +402,22 @@ class OBSavePostModalState extends OBContextualSearchBoxState { List _getImagePostActions() { return [ OBPillButton( - text: _localizationService.trans('post__create_photo'), + text: _localizationService.post__create_media, color: Pigment.fromString('#FCC14B'), icon: const OBIcon(OBIcons.photo), onPressed: () async { _unfocusTextField(); try { - File pickedPhoto = await _mediaService.pickImage( - imageType: OBImageType.post, - context: context, - flattenGifs: false); - if (pickedPhoto != null) { - bool photoIsGif = await _mediaService.isGif(pickedPhoto); - if (photoIsGif) { - _mediaService.convertGifToVideo(pickedPhoto).then( - (file) => _setPostVideoFile(file), - onError: (error, trace) => throw error); + var pickedMedia = await _mediaService.pickMedia( + context: context, + source: ImageSource.gallery, + flattenGifs: false, + ); + if (pickedMedia != null) { + if (pickedMedia.type == FileType.image) { + _setPostImageFile(pickedMedia.file); } else { - _setPostImageFile(pickedPhoto); + _setPostVideoFile(pickedMedia.file); } } } catch (error) { @@ -427,14 +426,21 @@ class OBSavePostModalState extends OBContextualSearchBoxState { }, ), OBPillButton( - text: _localizationService.post__create_video, + text: _localizationService.post__create_camera, color: Pigment.fromString('#50b1f2'), icon: const OBIcon(OBIcons.video), onPressed: () async { _unfocusTextField(); try { - File pickedVideo = await _mediaService.pickVideo(context: context); - if (pickedVideo != null) _setPostVideoFile(pickedVideo); + var pickedMedia = await _mediaService.pickMedia( + context: context, source: ImageSource.camera); + if (pickedMedia != null) { + if (pickedMedia.type == FileType.image) { + _setPostImageFile(pickedMedia.file); + } else { + _setPostVideoFile(pickedMedia.file); + } + } } catch (error) { _onError(error); } diff --git a/lib/services/bottom_sheet.dart b/lib/services/bottom_sheet.dart index 84ac3e0df..9c9e2a65a 100644 --- a/lib/services/bottom_sheet.dart +++ b/lib/services/bottom_sheet.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:Okuna/models/circle.dart'; @@ -9,29 +10,30 @@ import 'package:Okuna/models/post_comment.dart'; import 'package:Okuna/models/post_comment_reaction.dart'; import 'package:Okuna/models/post_reaction.dart'; import 'package:Okuna/models/user.dart'; +import 'package:Okuna/pages/home/bottom_sheets/camera_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/community_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/community_type_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/confirm_action.dart'; import 'package:Okuna/pages/home/bottom_sheets/connection_circles_picker.dart'; +import 'package:Okuna/pages/home/bottom_sheets/follows_lists_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/hashtag_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/hashtags_display_setting_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/image_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/link_previews_setting_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart'; import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart'; -import 'package:Okuna/pages/home/bottom_sheets/post_comment_more_actions.dart'; -import 'package:Okuna/pages/home/bottom_sheets/follows_lists_picker.dart'; +import 'package:Okuna/pages/home/bottom_sheets/media_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/post_actions.dart'; -import 'package:Okuna/pages/home/bottom_sheets/user_actions/user_actions.dart'; -import 'package:Okuna/pages/home/bottom_sheets/video_picker.dart'; +import 'package:Okuna/pages/home/bottom_sheets/post_comment_more_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/react_to_post.dart'; import 'package:Okuna/pages/home/bottom_sheets/react_to_post_comment.dart'; +import 'package:Okuna/pages/home/bottom_sheets/user_actions/user_actions.dart'; +import 'package:Okuna/pages/home/bottom_sheets/video_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/videos_autoplay_setting_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/videos_sound_setting_picker.dart'; import 'package:Okuna/services/user_preferences.dart'; import 'package:Okuna/widgets/post/post.dart'; import 'package:flutter/material.dart'; -import 'dart:async'; import 'package:meta/meta.dart'; import 'media.dart'; @@ -275,6 +277,22 @@ class BottomSheetService { }); } + Future showMediaPicker({@required BuildContext context}) { + return _showModalBottomSheetApp( + context: context, + builder: (BuildContext context) { + return OBMediaPickerBottomSheet(); + }); + } + + Future showCameraPicker({@required BuildContext context}) { + return _showModalBottomSheetApp( + context: context, + builder: (BuildContext context) { + return OBCameraPickerBottomSheet(); + }); + } + Future showVideoPicker({@required BuildContext context}) { return _showModalBottomSheetApp( context: context, diff --git a/lib/services/localization.dart b/lib/services/localization.dart index f3bb7f79d..0be248131 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -1918,6 +1918,14 @@ class LocalizationService { return Intl.message("Next", name: 'post__create_next'); } + String get post__create_media { + return Intl.message("Media", name: 'post__create_media'); + } + + String get post__create_camera { + return Intl.message("Camera", name: 'post__create_media'); + } + String get post__create_photo { return Intl.message("Photo", name: 'post__create_photo'); } diff --git a/lib/services/media.dart b/lib/services/media.dart index 4f45e9d03..df97e940b 100644 --- a/lib/services/media.dart +++ b/lib/services/media.dart @@ -3,13 +3,16 @@ import 'dart:io'; import 'package:Okuna/plugins/image_converter/image_converter.dart'; import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/utils_service.dart'; import 'package:Okuna/services/validation.dart'; import 'package:async/async.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:image_cropper/image_cropper.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; @@ -33,6 +36,7 @@ class MediaService { ValidationService _validationService; BottomSheetService _bottomSheetService; LocalizationService _localizationService; + ToastService _toastService; UtilsService _utilsService; void setLocalizationService(LocalizationService localizationService) { @@ -51,63 +55,120 @@ class MediaService { _bottomSheetService = modalService; } - Future pickImage( - {@required OBImageType imageType, - @required BuildContext context, - bool flattenGifs = true}) async { - File pickedImage = - await _bottomSheetService.showImagePicker(context: context); + void setToastService(ToastService toastService) { + _toastService = toastService; + } - if (pickedImage == null) return null; + Future pickMedia( + {@required BuildContext context, + @required ImageSource source, + bool flattenGifs}) async { + Media media; - pickedImage = await fixExifRotation(pickedImage); - final tempPath = await _getTempPath(); + if (source == ImageSource.gallery) { + media = await _bottomSheetService.showMediaPicker(context: context); + } else if (source == ImageSource.camera) { + media = await _bottomSheetService.showCameraPicker(context: context); + } else { + throw 'Unsupported media source: $source'; + } - final String processedImageUuid = _uuid.v4(); - String imageExtension = basename(pickedImage.path); + if (media == null) { + return null; + } - // The image picker gives us the real image, lets copy it into a temp path - pickedImage = - pickedImage.copySync('$tempPath/$processedImageUuid$imageExtension'); + return _prepareMedia( + media: media, context: context, flattenGifs: flattenGifs); + } - File processedPickedImage; + Future _prepareMedia( + {@required Media media, + @required BuildContext context, + bool flattenGifs = false, + OBImageType imageType = OBImageType.post}) async { + var mediaType = media.type; + Media result; - bool pickedImageIsGif = await isGif(pickedImage); + // Copy the media to a temporary location. + final tempPath = await _getTempPath(); + final String mediaUuid = _uuid.v4(); + String mediaExtension = basename(media.file.path); + var copiedMedia = + media.file.copySync('$tempPath/$mediaUuid$mediaExtension'); + + if (await isGif(media.file) && !flattenGifs) { + mediaType = FileType.video; + + Completer completer = Completer(); + convertGifToVideo(copiedMedia).then((file) => completer.complete(file), + onError: (error, trace) { + print(error); + _toastService.error( + message: _localizationService.error__unknown_error, + context: context); + }); + copiedMedia = await completer.future; + } - if (!pickedImageIsGif || flattenGifs) { - String processedImageName = processedImageUuid + '.jpg'; - processedPickedImage = File('$tempPath/$processedImageName'); + //TODO(komposten): Split into _prepareImage and _prepareVideo. + if (mediaType == FileType.image) { + copiedMedia = await fixExifRotation(copiedMedia, deleteOriginal: true); + String processedImageName = mediaUuid + '.jpg'; + File processedImage = File('$tempPath/$processedImageName'); List convertedImageData = - await ImageConverter.convertImage(pickedImage.readAsBytesSync()); - processedPickedImage.writeAsBytesSync(convertedImageData); - } else { - String processedImageName = processedImageUuid + '.gif'; - processedPickedImage = - pickedImage.copySync('$tempPath/$processedImageName'); - } + await ImageConverter.convertImage(copiedMedia.readAsBytesSync()); + processedImage.writeAsBytesSync(convertedImageData); + + // We have a new processed copy, so we can delete our first copy. + copiedMedia.deleteSync(); + + if (!await _validationService.isImageAllowedSize( + processedImage, imageType)) { + throw FileTooLargeException( + _validationService.getAllowedImageSize(imageType)); + } - // We now have a processed one - pickedImage.deleteSync(); + processedImage = await processImage(processedImage); - if (!await _validationService.isImageAllowedSize( - processedPickedImage, imageType)) { - throw FileTooLargeException( - _validationService.getAllowedImageSize(imageType)); + if (imageType == OBImageType.post) { + result = Media(processedImage, mediaType); + } else { + double ratioX = IMAGE_RATIOS[imageType]['x']; + double ratioY = IMAGE_RATIOS[imageType]['y']; + + File croppedFile = + await cropImage(processedImage, ratioX: ratioX, ratioY: ratioY); + + result = Media(croppedFile, mediaType); + } + } else if (mediaType == FileType.video) { + if (!await _validationService.isVideoAllowedSize(copiedMedia)) { + throw FileTooLargeException(_validationService.getAllowedVideoSize()); + } + + result = Media(copiedMedia, mediaType); + } else { + throw 'Unsupported media type: ${media.type}'; } - processedPickedImage = !pickedImageIsGif || flattenGifs - ? await processImage(processedPickedImage) - : processedPickedImage; + return result; + } - if (imageType == OBImageType.post) return processedPickedImage; + Future pickImage( + {@required OBImageType imageType, @required BuildContext context}) async { + File pickedImage = + await _bottomSheetService.showImagePicker(context: context); - double ratioX = IMAGE_RATIOS[imageType]['x']; - double ratioY = IMAGE_RATIOS[imageType]['y']; + if (pickedImage == null) return null; - File croppedFile = - await cropImage(processedPickedImage, ratioX: ratioX, ratioY: ratioY); + var media = await _prepareMedia( + media: Media(pickedImage, FileType.image), + context: context, + flattenGifs: true, + imageType: imageType, + ); - return croppedFile; + return media.file; } Future pickVideo({@required BuildContext context}) async { @@ -116,17 +177,12 @@ class MediaService { if (pickedVideo == null) return null; - String videoExtension = basename(pickedVideo.path); - String tmpImageName = _uuid.v4() + videoExtension; - final path = await _getTempPath(); - final String pickedVideoCopyPath = '$path/$tmpImageName'; - File pickedVideoCopy = pickedVideo.copySync(pickedVideoCopyPath); - - if (!await _validationService.isVideoAllowedSize(pickedVideoCopy)) { - throw FileTooLargeException(_validationService.getAllowedVideoSize()); - } + var media = await _prepareMedia( + media: Media(pickedVideo, FileType.video), + context: context, + ); - return pickedVideoCopy; + return media.file; } Future processImage(File image) async { @@ -148,13 +204,11 @@ class MediaService { await fixedImage.writeAsBytes(result); - if(deleteOriginal) await image.delete(); + if (deleteOriginal) await image.delete(); return fixedImage; } - - Future copyMediaFile(File mediaFile, {deleteOriginal: true}) async { final String processedImageUuid = _uuid.v4(); String imageExtension = basename(mediaFile.path); @@ -320,3 +374,10 @@ class FileTooLargeException implements Exception { } enum OBImageType { avatar, cover, post } + +class Media { + final File file; + final FileType type; + + const Media(this.file, this.type); +} diff --git a/lib/widgets/icon.dart b/lib/widgets/icon.dart index 5411eec53..137278bfe 100644 --- a/lib/widgets/icon.dart +++ b/lib/widgets/icon.dart @@ -194,7 +194,10 @@ class OBIcons { static const report = OBIconData(nativeIcon: Icons.flag); static const filter = OBIconData(nativeIcon: Icons.tune); static const gallery = OBIconData(nativeIcon: Icons.apps); + static const movie = OBIconData(nativeIcon: Icons.local_movies); + static const picture = OBIconData(nativeIcon: Icons.image); static const camera = OBIconData(nativeIcon: Icons.camera_alt); + static const video_camera = OBIconData(nativeIcon: Icons.videocam); static const privateCommunity = OBIconData(nativeIcon: Icons.lock); static const publicCommunity = OBIconData(nativeIcon: Icons.public); static const communityDescription = OBIconData(nativeIcon: Icons.book); From fa02f00551a91f925250fd40a9d33b32c4d4962f Mon Sep 17 00:00:00 2001 From: Komposten Date: Tue, 5 May 2020 19:57:19 +0200 Subject: [PATCH 28/39] :recycle: Re-factor media picking methods in MediaService --- lib/services/media.dart | 133 ++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 61 deletions(-) diff --git a/lib/services/media.dart b/lib/services/media.dart index df97e940b..835ac0c26 100644 --- a/lib/services/media.dart +++ b/lib/services/media.dart @@ -81,6 +81,37 @@ class MediaService { media: media, context: context, flattenGifs: flattenGifs); } + Future pickImage( + {@required OBImageType imageType, @required BuildContext context}) async { + File pickedImage = + await _bottomSheetService.showImagePicker(context: context); + + if (pickedImage == null) return null; + + var media = await _prepareMedia( + media: Media(pickedImage, FileType.image), + context: context, + flattenGifs: true, + imageType: imageType, + ); + + return media.file; + } + + Future pickVideo({@required BuildContext context}) async { + File pickedVideo = + await _bottomSheetService.showVideoPicker(context: context); + + if (pickedVideo == null) return null; + + var media = await _prepareMedia( + media: Media(pickedVideo, FileType.video), + context: context, + ); + + return media.file; + } + Future _prepareMedia( {@required Media media, @required BuildContext context, @@ -93,60 +124,27 @@ class MediaService { final tempPath = await _getTempPath(); final String mediaUuid = _uuid.v4(); String mediaExtension = basename(media.file.path); - var copiedMedia = - media.file.copySync('$tempPath/$mediaUuid$mediaExtension'); + var copiedFile = media.file.copySync('$tempPath/$mediaUuid$mediaExtension'); if (await isGif(media.file) && !flattenGifs) { mediaType = FileType.video; Completer completer = Completer(); - convertGifToVideo(copiedMedia).then((file) => completer.complete(file), + convertGifToVideo(copiedFile).then((file) => completer.complete(file), onError: (error, trace) { print(error); _toastService.error( message: _localizationService.error__unknown_error, context: context); }); - copiedMedia = await completer.future; + copiedFile = await completer.future; } - //TODO(komposten): Split into _prepareImage and _prepareVideo. + Media copiedMedia = Media(copiedFile, mediaType); if (mediaType == FileType.image) { - copiedMedia = await fixExifRotation(copiedMedia, deleteOriginal: true); - String processedImageName = mediaUuid + '.jpg'; - File processedImage = File('$tempPath/$processedImageName'); - List convertedImageData = - await ImageConverter.convertImage(copiedMedia.readAsBytesSync()); - processedImage.writeAsBytesSync(convertedImageData); - - // We have a new processed copy, so we can delete our first copy. - copiedMedia.deleteSync(); - - if (!await _validationService.isImageAllowedSize( - processedImage, imageType)) { - throw FileTooLargeException( - _validationService.getAllowedImageSize(imageType)); - } - - processedImage = await processImage(processedImage); - - if (imageType == OBImageType.post) { - result = Media(processedImage, mediaType); - } else { - double ratioX = IMAGE_RATIOS[imageType]['x']; - double ratioY = IMAGE_RATIOS[imageType]['y']; - - File croppedFile = - await cropImage(processedImage, ratioX: ratioX, ratioY: ratioY); - - result = Media(croppedFile, mediaType); - } + result = await _prepareImage(copiedMedia, tempPath, mediaUuid, imageType); } else if (mediaType == FileType.video) { - if (!await _validationService.isVideoAllowedSize(copiedMedia)) { - throw FileTooLargeException(_validationService.getAllowedVideoSize()); - } - - result = Media(copiedMedia, mediaType); + result = await _prepareVideo(copiedMedia); } else { throw 'Unsupported media type: ${media.type}'; } @@ -154,35 +152,48 @@ class MediaService { return result; } - Future pickImage( - {@required OBImageType imageType, @required BuildContext context}) async { - File pickedImage = - await _bottomSheetService.showImagePicker(context: context); + Future _prepareImage(Media media, String tempPath, String mediaUuid, + OBImageType imageType) async { + var image = await fixExifRotation(media.file, deleteOriginal: true); + String processedImageName = mediaUuid + '.jpg'; + File processedImage = File('$tempPath/$processedImageName'); + List convertedImageData = + await ImageConverter.convertImage(image.readAsBytesSync()); + processedImage.writeAsBytesSync(convertedImageData); + + // We have a new processed copy, so we can delete our first copy. + image.deleteSync(); + + if (!await _validationService.isImageAllowedSize( + processedImage, imageType)) { + throw FileTooLargeException( + _validationService.getAllowedImageSize(imageType)); + } - if (pickedImage == null) return null; + processedImage = await processImage(processedImage); - var media = await _prepareMedia( - media: Media(pickedImage, FileType.image), - context: context, - flattenGifs: true, - imageType: imageType, - ); + Media result; + if (imageType == OBImageType.post) { + result = Media(processedImage, media.type); + } else { + double ratioX = IMAGE_RATIOS[imageType]['x']; + double ratioY = IMAGE_RATIOS[imageType]['y']; - return media.file; - } + File croppedFile = + await cropImage(processedImage, ratioX: ratioX, ratioY: ratioY); - Future pickVideo({@required BuildContext context}) async { - File pickedVideo = - await _bottomSheetService.showVideoPicker(context: context); + result = Media(croppedFile, media.type); + } - if (pickedVideo == null) return null; + return result; + } - var media = await _prepareMedia( - media: Media(pickedVideo, FileType.video), - context: context, - ); + Future _prepareVideo(Media media) async { + if (!await _validationService.isVideoAllowedSize(media.file)) { + throw FileTooLargeException(_validationService.getAllowedVideoSize()); + } - return media.file; + return media; } Future processImage(File image) async { From e76648ee027db53c7dfe1d5ae1f2b2ee5df2113c Mon Sep 17 00:00:00 2001 From: Komposten Date: Tue, 5 May 2020 20:25:24 +0200 Subject: [PATCH 29/39] :truck: Move MediaService into a folder, extract Media to its own file --- .../home/bottom_sheets/camera_picker.dart | 7 ++-- .../home/bottom_sheets/image_picker.dart | 2 +- .../home/bottom_sheets/media_picker.dart | 7 ++-- .../home/bottom_sheets/video_picker.dart | 2 +- lib/pages/home/home.dart | 2 +- lib/pages/home/modals/save_community.dart | 2 +- .../home/modals/save_post/create_post.dart | 2 +- .../edit_profile/modals/edit_profile.dart | 2 +- lib/provider.dart | 2 +- lib/services/bottom_sheet.dart | 7 ++-- lib/services/{ => media}/media.dart | 39 ++++++++----------- lib/services/media/models/media_file.dart | 10 +++++ lib/services/share.dart | 3 +- lib/services/validation.dart | 2 +- lib/widgets/new_post_data_uploader.dart | 2 +- 15 files changed, 47 insertions(+), 44 deletions(-) rename lib/services/{ => media}/media.dart (93%) create mode 100644 lib/services/media/models/media_file.dart diff --git a/lib/pages/home/bottom_sheets/camera_picker.dart b/lib/pages/home/bottom_sheets/camera_picker.dart index b515e5762..6c6210f17 100644 --- a/lib/pages/home/bottom_sheets/camera_picker.dart +++ b/lib/pages/home/bottom_sheets/camera_picker.dart @@ -2,7 +2,8 @@ import 'dart:io'; import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; +import 'package:Okuna/services/media/models/media_file.dart'; import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/theming/text.dart'; import 'package:file_picker/file_picker.dart'; @@ -29,7 +30,7 @@ class OBCameraPickerBottomSheet extends StatelessWidget { if (permissionGranted) { File file = await ImagePicker.pickImage(source: ImageSource.camera); Navigator.pop( - context, file != null ? Media(file, FileType.image) : null); + context, file != null ? MediaFile(file, FileType.image) : null); } }, ), @@ -44,7 +45,7 @@ class OBCameraPickerBottomSheet extends StatelessWidget { if (permissionGranted) { File file = await ImagePicker.pickVideo(source: ImageSource.camera); Navigator.pop( - context, file != null ? Media(file, FileType.video) : null); + context, file != null ? MediaFile(file, FileType.video) : null); } }, ), diff --git a/lib/pages/home/bottom_sheets/image_picker.dart b/lib/pages/home/bottom_sheets/image_picker.dart index 9110abf01..790eda3db 100644 --- a/lib/pages/home/bottom_sheets/image_picker.dart +++ b/lib/pages/home/bottom_sheets/image_picker.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/theming/text.dart'; import 'package:file_picker/file_picker.dart'; diff --git a/lib/pages/home/bottom_sheets/media_picker.dart b/lib/pages/home/bottom_sheets/media_picker.dart index 8bebd0d4b..d2b6c666d 100644 --- a/lib/pages/home/bottom_sheets/media_picker.dart +++ b/lib/pages/home/bottom_sheets/media_picker.dart @@ -2,7 +2,8 @@ import 'dart:io'; import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; +import 'package:Okuna/services/media/models/media_file.dart'; import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/theming/text.dart'; import 'package:file_picker/file_picker.dart'; @@ -29,7 +30,7 @@ class OBMediaPickerBottomSheet extends StatelessWidget { if (permissionGranted) { File file = await FilePicker.getFile(type: FileType.image); Navigator.pop( - context, file != null ? Media(file, FileType.image) : null); + context, file != null ? MediaFile(file, FileType.image) : null); } }, ), @@ -44,7 +45,7 @@ class OBMediaPickerBottomSheet extends StatelessWidget { if (permissionGranted) { File file = await FilePicker.getFile(type: FileType.video); Navigator.pop( - context, file != null ? Media(file, FileType.video) : null); + context, file != null ? MediaFile(file, FileType.video) : null); } }, ), diff --git a/lib/pages/home/bottom_sheets/video_picker.dart b/lib/pages/home/bottom_sheets/video_picker.dart index 1784617a3..f79b3739a 100644 --- a/lib/pages/home/bottom_sheets/video_picker.dart +++ b/lib/pages/home/bottom_sheets/video_picker.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/theming/text.dart'; import 'package:file_picker/file_picker.dart'; diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index d99a862bd..cbde4395a 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'package:Okuna/models/push_notification.dart'; import 'package:Okuna/pages/home/lib/poppable_page_controller.dart'; import 'package:Okuna/services/intercom.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/push_notifications/push_notifications.dart'; import 'package:Okuna/models/user.dart'; import 'package:Okuna/pages/home/pages/communities/communities.dart'; diff --git a/lib/pages/home/modals/save_community.dart b/lib/pages/home/modals/save_community.dart index 8675b6c6f..5c6c6a7d3 100644 --- a/lib/pages/home/modals/save_community.dart +++ b/lib/pages/home/modals/save_community.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:Okuna/models/category.dart'; import 'package:Okuna/models/community.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/theme_value_parser.dart'; import 'package:Okuna/widgets/avatars/avatar.dart'; diff --git a/lib/pages/home/modals/save_post/create_post.dart b/lib/pages/home/modals/save_post/create_post.dart index 00a92d070..6ce725b79 100644 --- a/lib/pages/home/modals/save_post/create_post.dart +++ b/lib/pages/home/modals/save_post/create_post.dart @@ -16,7 +16,7 @@ import 'package:Okuna/services/draft.dart'; import 'package:Okuna/services/httpie.dart'; import 'package:Okuna/services/link_preview.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/navigation_service.dart'; import 'package:Okuna/services/share.dart'; import 'package:Okuna/services/toast.dart'; diff --git a/lib/pages/home/pages/profile/pages/edit_profile/modals/edit_profile.dart b/lib/pages/home/pages/profile/pages/edit_profile/modals/edit_profile.dart index 6add12225..db4efed7e 100644 --- a/lib/pages/home/pages/profile/pages/edit_profile/modals/edit_profile.dart +++ b/lib/pages/home/pages/profile/pages/edit_profile/modals/edit_profile.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:Okuna/models/user.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/httpie.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/user.dart'; diff --git a/lib/provider.dart b/lib/provider.dart index 7d3d955a7..0fd2c38fd 100644 --- a/lib/provider.dart +++ b/lib/provider.dart @@ -17,6 +17,7 @@ import 'package:Okuna/services/hashtags_api.dart'; import 'package:Okuna/services/intercom.dart'; import 'package:Okuna/services/link_preview.dart'; import 'package:Okuna/services/moderation_api.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/notifications_api.dart'; import 'package:Okuna/services/permissions.dart'; import 'package:Okuna/services/push_notifications/push_notifications.dart'; @@ -28,7 +29,6 @@ import 'package:Okuna/services/emojis_api.dart'; import 'package:Okuna/services/environment_loader.dart'; import 'package:Okuna/services/follows_api.dart'; import 'package:Okuna/services/httpie.dart'; -import 'package:Okuna/services/media.dart'; import 'package:Okuna/services/follows_lists_api.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/modal_service.dart'; diff --git a/lib/services/bottom_sheet.dart b/lib/services/bottom_sheet.dart index 9c9e2a65a..bccaff9c5 100644 --- a/lib/services/bottom_sheet.dart +++ b/lib/services/bottom_sheet.dart @@ -31,13 +31,12 @@ import 'package:Okuna/pages/home/bottom_sheets/user_actions/user_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/video_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/videos_autoplay_setting_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/videos_sound_setting_picker.dart'; +import 'package:Okuna/services/media/models/media_file.dart'; import 'package:Okuna/services/user_preferences.dart'; import 'package:Okuna/widgets/post/post.dart'; import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; -import 'media.dart'; - class BottomSheetService { bool hasActiveBottomSheet = false; @@ -277,7 +276,7 @@ class BottomSheetService { }); } - Future showMediaPicker({@required BuildContext context}) { + Future showMediaPicker({@required BuildContext context}) { return _showModalBottomSheetApp( context: context, builder: (BuildContext context) { @@ -285,7 +284,7 @@ class BottomSheetService { }); } - Future showCameraPicker({@required BuildContext context}) { + Future showCameraPicker({@required BuildContext context}) { return _showModalBottomSheetApp( context: context, builder: (BuildContext context) { diff --git a/lib/services/media.dart b/lib/services/media/media.dart similarity index 93% rename from lib/services/media.dart rename to lib/services/media/media.dart index 835ac0c26..dbea624cc 100644 --- a/lib/services/media.dart +++ b/lib/services/media/media.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'dart:io'; import 'package:Okuna/plugins/image_converter/image_converter.dart'; +import 'package:Okuna/services/bottom_sheet.dart'; import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/media/models/media_file.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/utils_service.dart'; import 'package:Okuna/services/validation.dart'; @@ -19,8 +21,6 @@ import 'package:path_provider/path_provider.dart'; import 'package:uuid/uuid.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; -import 'bottom_sheet.dart'; - export 'package:image_picker/image_picker.dart'; class MediaService { @@ -59,11 +59,11 @@ class MediaService { _toastService = toastService; } - Future pickMedia( + Future pickMedia( {@required BuildContext context, @required ImageSource source, bool flattenGifs}) async { - Media media; + MediaFile media; if (source == ImageSource.gallery) { media = await _bottomSheetService.showMediaPicker(context: context); @@ -89,7 +89,7 @@ class MediaService { if (pickedImage == null) return null; var media = await _prepareMedia( - media: Media(pickedImage, FileType.image), + media: MediaFile(pickedImage, FileType.image), context: context, flattenGifs: true, imageType: imageType, @@ -105,20 +105,20 @@ class MediaService { if (pickedVideo == null) return null; var media = await _prepareMedia( - media: Media(pickedVideo, FileType.video), + media: MediaFile(pickedVideo, FileType.video), context: context, ); return media.file; } - Future _prepareMedia( - {@required Media media, + Future _prepareMedia( + {@required MediaFile media, @required BuildContext context, bool flattenGifs = false, OBImageType imageType = OBImageType.post}) async { var mediaType = media.type; - Media result; + MediaFile result; // Copy the media to a temporary location. final tempPath = await _getTempPath(); @@ -140,7 +140,7 @@ class MediaService { copiedFile = await completer.future; } - Media copiedMedia = Media(copiedFile, mediaType); + MediaFile copiedMedia = MediaFile(copiedFile, mediaType); if (mediaType == FileType.image) { result = await _prepareImage(copiedMedia, tempPath, mediaUuid, imageType); } else if (mediaType == FileType.video) { @@ -152,8 +152,8 @@ class MediaService { return result; } - Future _prepareImage(Media media, String tempPath, String mediaUuid, - OBImageType imageType) async { + Future _prepareImage(MediaFile media, String tempPath, + String mediaUuid, OBImageType imageType) async { var image = await fixExifRotation(media.file, deleteOriginal: true); String processedImageName = mediaUuid + '.jpg'; File processedImage = File('$tempPath/$processedImageName'); @@ -172,9 +172,9 @@ class MediaService { processedImage = await processImage(processedImage); - Media result; + MediaFile result; if (imageType == OBImageType.post) { - result = Media(processedImage, media.type); + result = MediaFile(processedImage, media.type); } else { double ratioX = IMAGE_RATIOS[imageType]['x']; double ratioY = IMAGE_RATIOS[imageType]['y']; @@ -182,13 +182,13 @@ class MediaService { File croppedFile = await cropImage(processedImage, ratioX: ratioX, ratioY: ratioY); - result = Media(croppedFile, media.type); + result = MediaFile(croppedFile, media.type); } return result; } - Future _prepareVideo(Media media) async { + Future _prepareVideo(MediaFile media) async { if (!await _validationService.isVideoAllowedSize(media.file)) { throw FileTooLargeException(_validationService.getAllowedVideoSize()); } @@ -385,10 +385,3 @@ class FileTooLargeException implements Exception { } enum OBImageType { avatar, cover, post } - -class Media { - final File file; - final FileType type; - - const Media(this.file, this.type); -} diff --git a/lib/services/media/models/media_file.dart b/lib/services/media/models/media_file.dart new file mode 100644 index 000000000..636ff9d16 --- /dev/null +++ b/lib/services/media/models/media_file.dart @@ -0,0 +1,10 @@ +import 'dart:io'; + +import 'package:file_picker/file_picker.dart'; + +class MediaFile { + final File file; + final FileType type; + + const MediaFile(this.file, this.type); +} diff --git a/lib/services/share.dart b/lib/services/share.dart index 29f0cca11..a77f2c850 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -3,14 +3,13 @@ import 'dart:io'; import 'package:Okuna/plugins/share/share.dart'; import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/validation.dart'; import 'package:async/async.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'media.dart'; - class ShareService { static const _stream = const EventChannel('openbook.social/receive_share'); diff --git a/lib/services/validation.dart b/lib/services/validation.dart index 783a6045d..656920f6c 100644 --- a/lib/services/validation.dart +++ b/lib/services/validation.dart @@ -4,7 +4,7 @@ import 'package:Okuna/services/communities_api.dart'; import 'package:Okuna/services/connections_circles_api.dart'; import 'package:Okuna/services/follows_lists_api.dart'; import 'package:Okuna/services/httpie.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/utils_service.dart'; import 'package:validators/validators.dart' as validators; diff --git a/lib/widgets/new_post_data_uploader.dart b/lib/widgets/new_post_data_uploader.dart index d3787c00d..c2362c591 100644 --- a/lib/widgets/new_post_data_uploader.dart +++ b/lib/widgets/new_post_data_uploader.dart @@ -6,7 +6,7 @@ import 'package:Okuna/models/community.dart'; import 'package:Okuna/models/post.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/user.dart'; import 'package:Okuna/widgets/theming/highlighted_box.dart'; import 'package:Okuna/widgets/theming/text.dart'; From 79e4c66ccab70be21adf93ba0bed08b97c232118 Mon Sep 17 00:00:00 2001 From: Komposten Date: Tue, 5 May 2020 20:51:12 +0200 Subject: [PATCH 30/39] :pencil: Add some documentation to the pickMedia/Image/Video methods --- lib/services/media/media.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart index dbea624cc..42ae42634 100644 --- a/lib/services/media/media.dart +++ b/lib/services/media/media.dart @@ -59,6 +59,13 @@ class MediaService { _toastService = toastService; } + /// Opens a bottom sheet with the options to pick either an image or a video. + /// The media is picked from the gallery or camera (determined by [source]). + /// + /// If a GIF is picked it will be converted to a video file before being returned. + /// + /// The returned file may be either an image or a video. Use [MediaFile.type] + /// to determine which one it is. Future pickMedia( {@required BuildContext context, @required ImageSource source, @@ -81,6 +88,11 @@ class MediaService { media: media, context: context, flattenGifs: flattenGifs); } + /// Opens a bottom sheet with the option to pick an image from gallery or snap + /// a new one with the camera. + /// + /// The returned file should always point to an image. If a GIF is picked it will + /// be flattened. Future pickImage( {@required OBImageType imageType, @required BuildContext context}) async { File pickedImage = @@ -98,6 +110,10 @@ class MediaService { return media.file; } + /// Opens a bottom sheet with the option to pick a video from gallery or take + /// a new one with the camera. + /// + /// The returned file should always point to a video. Future pickVideo({@required BuildContext context}) async { File pickedVideo = await _bottomSheetService.showVideoPicker(context: context); From 041059336f2b2dcf84c50384135c4b9e462d6925 Mon Sep 17 00:00:00 2001 From: Komposten Date: Tue, 5 May 2020 22:15:34 +0200 Subject: [PATCH 31/39] :fire: Remove the media picker bottom sheet and open the media picker directly instead --- .../home/bottom_sheets/media_picker.dart | 63 ------------------- lib/provider.dart | 1 + lib/services/bottom_sheet.dart | 9 --- lib/services/media/media.dart | 42 ++++++++++++- 4 files changed, 40 insertions(+), 75 deletions(-) delete mode 100644 lib/pages/home/bottom_sheets/media_picker.dart diff --git a/lib/pages/home/bottom_sheets/media_picker.dart b/lib/pages/home/bottom_sheets/media_picker.dart deleted file mode 100644 index d2b6c666d..000000000 --- a/lib/pages/home/bottom_sheets/media_picker.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'dart:io'; -import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; -import 'package:Okuna/provider.dart'; -import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media/media.dart'; -import 'package:Okuna/services/media/models/media_file.dart'; -import 'package:Okuna/widgets/icon.dart'; -import 'package:Okuna/widgets/theming/text.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; - -class OBMediaPickerBottomSheet extends StatelessWidget { - const OBMediaPickerBottomSheet({Key key}) : super(key: key); - - @override - Widget build(BuildContext context) { - var provider = OpenbookProvider.of(context); - - LocalizationService localizationService = provider.localizationService; - - List mediaPickerActions = [ - ListTile( - leading: const OBIcon(OBIcons.picture), - title: OBText( - localizationService.post__create_photo, - ), - onTap: () async { - bool permissionGranted = await provider.permissionService - .requestStoragePermissions(context: context); - if (permissionGranted) { - File file = await FilePicker.getFile(type: FileType.image); - Navigator.pop( - context, file != null ? MediaFile(file, FileType.image) : null); - } - }, - ), - ListTile( - leading: const OBIcon(OBIcons.movie), - title: OBText( - localizationService.post__create_video, - ), - onTap: () async { - bool permissionGranted = await provider.permissionService - .requestStoragePermissions(context: context); - if (permissionGranted) { - File file = await FilePicker.getFile(type: FileType.video); - Navigator.pop( - context, file != null ? MediaFile(file, FileType.video) : null); - } - }, - ), - ]; - - return OBRoundedBottomSheet( - child: Padding( - padding: EdgeInsets.only(bottom: 16), - child: Column( - children: mediaPickerActions, - mainAxisSize: MainAxisSize.min, - ), - )); - } -} diff --git a/lib/provider.dart b/lib/provider.dart index 0fd2c38fd..a9c265467 100644 --- a/lib/provider.dart +++ b/lib/provider.dart @@ -193,6 +193,7 @@ class OpenbookProviderState extends State { dialogService.setThemeValueParserService(themeValueParserService); mediaService.setValidationService(validationService); mediaService.setBottomSheetService(bottomSheetService); + mediaService.setPermissionsService(permissionService); mediaService.setUtilsService(utilsService); documentsService.setHttpService(httpService); moderationApiService.setStringTemplateService(stringTemplateService); diff --git a/lib/services/bottom_sheet.dart b/lib/services/bottom_sheet.dart index bccaff9c5..cf55179d9 100644 --- a/lib/services/bottom_sheet.dart +++ b/lib/services/bottom_sheet.dart @@ -22,7 +22,6 @@ import 'package:Okuna/pages/home/bottom_sheets/image_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/link_previews_setting_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart'; import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart'; -import 'package:Okuna/pages/home/bottom_sheets/media_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/post_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/post_comment_more_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/react_to_post.dart'; @@ -276,14 +275,6 @@ class BottomSheetService { }); } - Future showMediaPicker({@required BuildContext context}) { - return _showModalBottomSheetApp( - context: context, - builder: (BuildContext context) { - return OBMediaPickerBottomSheet(); - }); - } - Future showCameraPicker({@required BuildContext context}) { return _showModalBottomSheetApp( context: context, diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart index 42ae42634..b483a5329 100644 --- a/lib/services/media/media.dart +++ b/lib/services/media/media.dart @@ -5,6 +5,7 @@ import 'package:Okuna/plugins/image_converter/image_converter.dart'; import 'package:Okuna/services/bottom_sheet.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/media/models/media_file.dart'; +import 'package:Okuna/services/permissions.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/utils_service.dart'; import 'package:Okuna/services/validation.dart'; @@ -37,6 +38,7 @@ class MediaService { BottomSheetService _bottomSheetService; LocalizationService _localizationService; ToastService _toastService; + PermissionsService _permissionsService; UtilsService _utilsService; void setLocalizationService(LocalizationService localizationService) { @@ -59,6 +61,10 @@ class MediaService { _toastService = toastService; } + void setPermissionsService(PermissionsService permissionsService) { + _permissionsService = permissionsService; + } + /// Opens a bottom sheet with the options to pick either an image or a video. /// The media is picked from the gallery or camera (determined by [source]). /// @@ -73,7 +79,16 @@ class MediaService { MediaFile media; if (source == ImageSource.gallery) { - media = await _bottomSheetService.showMediaPicker(context: context); + bool permissionGranted = + await _permissionsService.requestStoragePermissions(context: context); + if (permissionGranted) { + File file = await FilePicker.getFile(type: FileType.media); + + if (file != null) { + FileType type = await getMediaType(file); + media = MediaFile(file, type); + } + } } else if (source == ImageSource.camera) { media = await _bottomSheetService.showCameraPicker(context: context); } else { @@ -84,8 +99,13 @@ class MediaService { return null; } - return _prepareMedia( - media: media, context: context, flattenGifs: flattenGifs); + media = await _prepareMedia( + media: media, + context: context, + flattenGifs: flattenGifs, + ); + + return media; } /// Opens a bottom sheet with the option to pick an image from gallery or snap @@ -372,6 +392,22 @@ class MediaService { return mediaMimeSubtype == 'gif'; } + Future getMediaType(File file) async { + String mediaMime = await _utilsService.getFileMimeType(file); + + String mediaMimeType = mediaMime.split('/')[0]; + + if (mediaMimeType == 'video') { + return FileType.video; + } else if (mediaMimeType == 'image') { + return FileType.image; + } else if (mediaMimeType == 'audio') { + return FileType.audio; + } else { + return null; + } + } + Future cropImage(File image, {double ratioX, double ratioY}) async { return ImageCropper.cropImage( sourcePath: image.path, From 45755cdfd82fd79ade3e0fc8fda2298e7f20c879 Mon Sep 17 00:00:00 2001 From: Komposten Date: Tue, 5 May 2020 22:54:30 +0200 Subject: [PATCH 32/39] :globe_with_meridians: Update localisation --- assets/i18n/en/post.arb | 10 ++++++++++ lib/services/localization.dart | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/assets/i18n/en/post.arb b/assets/i18n/en/post.arb index c8b402467..a0a5e94fa 100644 --- a/assets/i18n/en/post.arb +++ b/assets/i18n/en/post.arb @@ -231,6 +231,16 @@ "type": "text", "placeholders": {} }, + "create_media": "Media", + "@create_media": { + "type": "text", + "placeholders": {} + }, + "create_camera": "Camera", + "@create_camera": { + "type": "text", + "placeholders": {} + }, "create_photo": "Photo", "@create_photo": { "type": "text", diff --git a/lib/services/localization.dart b/lib/services/localization.dart index 0be248131..eea05f5d0 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -1923,7 +1923,7 @@ class LocalizationService { } String get post__create_camera { - return Intl.message("Camera", name: 'post__create_media'); + return Intl.message("Camera", name: 'post__create_camera'); } String get post__create_photo { From 07826573ebb2d8b14d0708f35b62c2337aa699b7 Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Wed, 6 May 2020 13:57:03 +0200 Subject: [PATCH 33/39] Revert "Merge pull request #517 from OkunaOrg/feature/subscribe-to-post-notifications" This reverts commit 7d4a2e5559db5380108a8041d3a8e43a787797ca, reversing changes made to bd854f709667970cc56c753db3a5591b2f8c1a45. --- assets/i18n/en/notifications.arb | 68 ++++-- assets/i18n/en/post.arb | 105 -------- lib/models/post.dart | 24 +- lib/models/post_comment.dart | 28 +-- ...st_comment_notifications_subscription.dart | 66 ------ .../post_notifications_subscription.dart | 73 ------ lib/models/user.dart | 62 ++--- lib/models/user_notifications_settings.dart | 54 ++++- .../manage_post_comment_notifications.dart | 224 ------------------ .../manage_post_notifications.dart | 224 ------------------ .../widgets/manage_notifications.dart | 162 ------------- .../home/bottom_sheets/post_actions.dart | 16 +- .../post_comment_more_actions.dart | 6 +- .../post_comment_reply_expanded.dart | 9 +- .../pages/notifications_settings.dart | 179 ++++++++++++-- .../widgets/post_comment_actions.dart | 17 +- lib/services/auth_api.dart | 63 +++-- lib/services/bottom_sheet.dart | 39 +-- lib/services/localization.dart | 183 +++++--------- lib/services/modal_service.dart | 2 - lib/services/posts_api.dart | 85 ------- lib/services/user.dart | 82 +------ lib/widgets/fields/toggle_field.dart | 12 +- ...anage_post_comment_notifications_tile.dart | 73 ------ .../manage_post_notifications_tile.dart | 70 ------ 25 files changed, 424 insertions(+), 1502 deletions(-) delete mode 100644 lib/models/post_comment_notifications_subscription.dart delete mode 100644 lib/models/post_notifications_subscription.dart delete mode 100644 lib/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart delete mode 100644 lib/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart delete mode 100644 lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart delete mode 100644 lib/widgets/tiles/actions/manage_post_comment_notifications_tile.dart delete mode 100644 lib/widgets/tiles/actions/manage_post_notifications_tile.dart diff --git a/assets/i18n/en/notifications.arb b/assets/i18n/en/notifications.arb index 46facd029..9041b8a3e 100644 --- a/assets/i18n/en/notifications.arb +++ b/assets/i18n/en/notifications.arb @@ -44,13 +44,63 @@ "type": "text", "placeholders": {} }, - "post_notifications_title": "Post notifications", - "@post_notifications_title": { + "comment_title": "Post comment", + "@comment_title": { "type": "text", "placeholders": {} }, - "post_notifications_desc": "Be notified of all post related notifications - comments, replies, reactions and user mentions", - "@post_notifications_desc": { + "comment_desc": "Be notified when someone comments on one of your posts or one you also commented", + "@comment_desc": { + "type": "text", + "placeholders": {} + }, + "comment_reply_title": "Post comment reply", + "@comment_reply_title": { + "type": "text", + "placeholders": {} + }, + "comment_reply_desc": "Be notified when someone replies to one of your comments or one you also replied to", + "@comment_reply_desc": { + "type": "text", + "placeholders": {} + }, + "comment_user_mention_title": "Post comment mention", + "@comment_user_mention_title": { + "type": "text", + "placeholders": {} + }, + "comment_user_mention_desc": "Be notified when someone mentions you on one of their comments", + "@comment_user_mention_desc": { + "type": "text", + "placeholders": {} + }, + "post_user_mention_title": "Post mention", + "@post_user_mention_title": { + "type": "text", + "placeholders": {} + }, + "post_user_mention_desc": "Be notified when someone mentions you on one of their posts", + "@post_user_mention_desc": { + "type": "text", + "placeholders": {} + }, + "comment_reaction_title": "Post comment reaction", + "@comment_reaction_title": { + "type": "text", + "placeholders": {} + }, + "comment_reaction_desc": "Be notified when someone reacts to one of your post commments", + "@comment_reaction_desc": { + "type": "text", + "placeholders": {} + }, + "post_reaction_title": "Post reaction", + "@post_reaction_title": { + "type": "text", + "placeholders": {} + }, + "post_reaction_desc": "Be notified when someone reacts to one of your posts", + "@post_reaction_desc": { "type": "text", "placeholders": {} }, @@ -94,16 +144,6 @@ "type": "text", "placeholders": {} }, - "enable_comment_post_notifications": "Enable new comment notifications", - "@enable_comment_post_notifications": { - "type": "text", - "placeholders": {} - }, - "disable_comment_post_notifications": "Disable new comment notifications", - "@disable_comment_post_notifications": { - "type": "text", - "placeholders": {} - }, "mute_post_turn_on_post_comment_notifications": "Turn on post comment notifications", "@mute_post_turn_on_post_comment_notifications": { "type": "text", diff --git a/assets/i18n/en/post.arb b/assets/i18n/en/post.arb index c8b402467..f221acf6c 100644 --- a/assets/i18n/en/post.arb +++ b/assets/i18n/en/post.arb @@ -523,111 +523,6 @@ "type": "text", "placeholders": {} }, - "manage_post_notifications": "Manage post notifications", - "@manage_post_notifications": { - "type": "text", - "placeholders": {} - }, - "manage_post_comment_notifications": "Manage comment notifications", - "@manage_post_comment_notifications": { - "type": "text", - "placeholders": {} - }, - "subscribe_post_notifications": "Subscribe to post notifications", - "@subscribe_post_notifications": { - "type": "text", - "placeholders": {} - }, - "subscribe_post_comment_notifications": "Subscribe to comment notifications", - "@subscribe_post_comment_notifications": { - "type": "text", - "placeholders": {} - }, - "mute_post_notifications_text": "Mute post", - "@mute_post_notifications_text": { - "type": "text", - "placeholders": {} - }, - "unmute_post_notifications_text": "Unmute post", - "@unmute_post_notifications_text": { - "type": "text", - "placeholders": {} - }, - "mute_post_comment_notifications_text": "Mute comment", - "@mute_post_comment_notifications_text": { - "type": "text", - "placeholders": {} - }, - "unmute_post_comment_notifications_text": "Unmute comment", - "@unmute_post_comment_notifications_text": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_comments_title": "Comments", - "@manage_notifications_comments_title": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_comments_desc": "Get notifications when someone comments on this post", - "@manage_notifications_comments_desc": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_comment_reactions_title": "Comment Reactions", - "@manage_notifications_comment_reactions_title": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_comment_reactions_desc": "Get notifications when someone reacts to your comments", - "@manage_notifications_comment_reactions_desc": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_reactions_title": "Reactions", - "@manage_notifications_reactions_title": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_reactions_desc": "Get notifications when someone reacts to your post", - "@manage_notifications_reactions_desc": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_reactions_post_comment_desc": "Get notifications when someone reacts to your comment", - "@manage_notifications_reactions_post_comment_desc": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_replies_title": "Replies", - "@manage_notifications_replies_title": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_replies_desc": "Get notifications when someone replies to comments on this post", - "@manage_notifications_replies_desc": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_replies_post_comment_desc": "Get notifications when someone replies to this comment", - "@manage_notifications_replies_post_comment_desc": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_successfully_saved": "Notification settings saved", - "@manage_notifications_successfully_saved": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_successfully_muted": "Notifications muted", - "@manage_notifications_successfully_muted": { - "type": "text", - "placeholders": {} - }, - "manage_notifications_successfully_unmuted": "Notifications unmuted", - "@manage_notifications_successfully_unmuted": { - "type": "text", - "placeholders": {} - }, "comments_enabled_message": "Comments enabled for post", "@comments_enabled_message": { "type": "text", diff --git a/lib/models/post.dart b/lib/models/post.dart index 5ae221d02..46dc35254 100644 --- a/lib/models/post.dart +++ b/lib/models/post.dart @@ -8,7 +8,6 @@ import 'package:Okuna/models/post_comment.dart'; import 'package:Okuna/models/post_comment_list.dart'; import 'package:Okuna/models/post_media.dart'; import 'package:Okuna/models/post_media_list.dart'; -import 'package:Okuna/models/post_notifications_subscription.dart'; import 'package:Okuna/models/post_preview_link_data.dart'; import 'package:Okuna/models/post_reaction.dart'; import 'package:Okuna/models/reactions_emoji_count.dart'; @@ -27,7 +26,6 @@ class Post extends UpdatableModel { DateTime created; User creator; CirclesList circles; - PostNotificationsSubscription postNotificationsSubscription; ReactionsEmojiCountList reactionsEmojiCounts; PostReaction reaction; @@ -104,8 +102,7 @@ class Post extends UpdatableModel { 'is_encircled': isEncircled, 'is_edited': isEdited, 'is_closed': isClosed, - 'is_reported': isReported, - 'post_notifications_subscription': postNotificationsSubscription + 'is_reported': isReported }; } @@ -132,7 +129,6 @@ class Post extends UpdatableModel { this.reaction, this.reactionsEmojiCounts, this.areCommentsEnabled, - this.postNotificationsSubscription, this.circles, this.community, this.status, @@ -215,11 +211,6 @@ class Post extends UpdatableModel { if (json.containsKey('circles')) circles = factory.parseCircles(json['circles']); - - if (json.containsKey('post_notifications_subscription')) - postNotificationsSubscription = - factory.parsePostNotificationsSubscription( - json['post_notifications_subscription']); } void updatePreviewDataFromJson(Map json) { @@ -325,10 +316,6 @@ class Post extends UpdatableModel { return creator.id; } - PostNotificationsSubscription getNotificationsSubscription() { - return this.postNotificationsSubscription; - } - String getCreatorAvatar() { return creator.profile.avatar; } @@ -479,8 +466,6 @@ class PostFactory extends UpdatableModelFactory { isEncircled: json['is_encircled'], isEdited: json['is_edited'], isClosed: json['is_closed'], - postNotificationsSubscription: parsePostNotificationsSubscription( - json['post_notifications_subscription']), reactionsEmojiCounts: parseReactionsEmojiCounts(json['reactions_emoji_counts'])); } @@ -535,13 +520,6 @@ class PostFactory extends UpdatableModelFactory { return CirclesList.fromJson(circlesData); } - PostNotificationsSubscription parsePostNotificationsSubscription( - Map postNotificationsSubscriptionData) { - if (postNotificationsSubscriptionData == null) return null; - return PostNotificationsSubscription.fromJSON( - postNotificationsSubscriptionData); - } - Language parseLanguage(Map languageData) { if (languageData == null) return null; return Language.fromJson(languageData); diff --git a/lib/models/post_comment.dart b/lib/models/post_comment.dart index 3e72f5ad0..002d51d31 100644 --- a/lib/models/post_comment.dart +++ b/lib/models/post_comment.dart @@ -2,7 +2,6 @@ import 'package:Okuna/models/hashtag.dart'; import 'package:Okuna/models/hashtags_list.dart'; import 'package:Okuna/models/post.dart'; import 'package:Okuna/models/post_comment_list.dart'; -import 'package:Okuna/models/post_comment_notifications_subscription.dart'; import 'package:Okuna/models/post_comment_reaction.dart'; import 'package:Okuna/models/reactions_emoji_count.dart'; import 'package:Okuna/models/reactions_emoji_count_list.dart'; @@ -28,7 +27,6 @@ class PostComment extends UpdatableModel { PostCommentReaction reaction; HashtagsList hashtagsList; Map hashtagsMap; - PostCommentNotificationsSubscription postCommentNotificationsSubscription; Post post; bool isEdited; @@ -83,8 +81,7 @@ class PostComment extends UpdatableModel { this.replies, this.repliesCount, this.reactionsEmojiCounts, - this.reaction, - this.postCommentNotificationsSubscription}) { + this.reaction}) { _updateHashtagsMap(); } @@ -117,9 +114,7 @@ class PostComment extends UpdatableModel { 'reactions_emoji_counts': reactionsEmojiCounts.counts .map((ReactionsEmojiCount reaction) => reaction.toJson()) ?.toList(), - 'reaction': reaction.toJson(), - 'post_comment_notifications_subscription': - postCommentNotificationsSubscription.toJson() + 'reaction': reaction.toJson() }; } @@ -183,12 +178,6 @@ class PostComment extends UpdatableModel { hashtagsList = factory.parseHashtagsList(json['hashtags']); _updateHashtagsMap(); } - - if (json.containsKey('post_comment_notifications_subscription')) { - postCommentNotificationsSubscription = - factory.parsePostCommentNotificationsSubscription( - json['post_comment_notifications_subscription']); - } } String getRelativeCreated() { @@ -342,10 +331,7 @@ class PostCommentFactory extends UpdatableModelFactory { reaction: parseReaction(json['reaction']), hashtagsList: parseHashtagsList(json['hashtags']), reactionsEmojiCounts: - parseReactionsEmojiCounts(json['reactions_emoji_counts']), - postCommentNotificationsSubscription: - parsePostCommentNotificationsSubscription( - json['post_comment_notifications_subscription'])); + parseReactionsEmojiCounts(json['reactions_emoji_counts'])); } Post parsePost(Map post) { @@ -392,14 +378,6 @@ class PostCommentFactory extends UpdatableModelFactory { if (hashtagsList == null) return null; return HashtagsList.fromJson(hashtagsList); } - - PostCommentNotificationsSubscription - parsePostCommentNotificationsSubscription( - Map postCommentNotificationsSubscriptionData) { - if (postCommentNotificationsSubscriptionData == null) return null; - return PostCommentNotificationsSubscription.fromJSON( - postCommentNotificationsSubscriptionData); - } } enum PostCommentsSortType { asc, dec } diff --git a/lib/models/post_comment_notifications_subscription.dart b/lib/models/post_comment_notifications_subscription.dart deleted file mode 100644 index 4c191a8cd..000000000 --- a/lib/models/post_comment_notifications_subscription.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:Okuna/models/updatable_model.dart'; -import 'package:dcache/dcache.dart'; - -class PostCommentNotificationsSubscription - extends UpdatableModel { - final int id; - final int postCommentId; - bool reactionNotifications; - bool replyNotifications; - - PostCommentNotificationsSubscription( - {this.id, - this.postCommentId, - this.reactionNotifications, - this.replyNotifications}); - - static final factory = PostCommentNotificationsSubscriptionFactory(); - - factory PostCommentNotificationsSubscription.fromJSON( - Map json) { - if (json == null) return null; - return factory.fromJson(json); - } - - Map toJson() { - return { - 'id': id, - 'post_comment': postCommentId, - 'reaction_notifications': reactionNotifications, - 'reply_notifications': replyNotifications, - }; - } - - Map getSettingsObject() { - return { - 'replyNotifications': this.replyNotifications, - 'reactionNotifications': this.reactionNotifications - }; - } - - @override - void updateFromJson(Map json) { - if (json.containsKey('reaction_notifications')) { - reactionNotifications = json['reaction_notifications']; - } - if (json.containsKey('reply_notifications')) { - replyNotifications = json['reply_notifications']; - } - } -} - -class PostCommentNotificationsSubscriptionFactory - extends UpdatableModelFactory { - @override - SimpleCache cache = - SimpleCache(storage: UpdatableModelSimpleStorage(size: 20)); - - @override - PostCommentNotificationsSubscription makeFromJson(Map json) { - return PostCommentNotificationsSubscription( - id: json['id'], - postCommentId: json['post_comment'], - reactionNotifications: json['reaction_notifications'], - replyNotifications: json['reply_notifications']); - } -} diff --git a/lib/models/post_notifications_subscription.dart b/lib/models/post_notifications_subscription.dart deleted file mode 100644 index 2263f46f6..000000000 --- a/lib/models/post_notifications_subscription.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:Okuna/models/updatable_model.dart'; -import 'package:dcache/dcache.dart'; - -class PostNotificationsSubscription - extends UpdatableModel { - final int id; - final int postId; - bool commentNotifications; - bool reactionNotifications; - bool replyNotifications; - - PostNotificationsSubscription( - {this.id, - this.postId, - this.commentNotifications, - this.reactionNotifications, - this.replyNotifications}); - - static final factory = PostNotificationsSubscriptionFactory(); - - factory PostNotificationsSubscription.fromJSON(Map json) { - if (json == null) return null; - return factory.fromJson(json); - } - - Map toJson() { - return { - 'id': id, - 'post': postId, - 'comment_notifications': commentNotifications, - 'reaction_notifications': reactionNotifications, - 'reply_notifications': replyNotifications, - }; - } - - Map getSettingsObject() { - return { - 'commentNotifications': this.commentNotifications, - 'replyNotifications': this.replyNotifications, - 'reactionNotifications': this.reactionNotifications - }; - } - - @override - void updateFromJson(Map json) { - if (json.containsKey('comment_notifications')) { - commentNotifications = json['comment_notifications']; - } - if (json.containsKey('reaction_notifications')) { - reactionNotifications = json['reaction_notifications']; - } - if (json.containsKey('reply_notifications')) { - replyNotifications = json['reply_notifications']; - } - } -} - -class PostNotificationsSubscriptionFactory - extends UpdatableModelFactory { - @override - SimpleCache cache = - SimpleCache(storage: UpdatableModelSimpleStorage(size: 20)); - - @override - PostNotificationsSubscription makeFromJson(Map json) { - return PostNotificationsSubscription( - id: json['id'], - postId: json['post'], - commentNotifications: json['comment_notifications'], - reactionNotifications: json['reaction_notifications'], - replyNotifications: json['reply_notifications']); - } -} diff --git a/lib/models/user.dart b/lib/models/user.dart index 7d549226e..0edb499ce 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -58,9 +58,9 @@ class User extends UpdatableModel { storage: UpdatableModelSimpleStorage(size: 10))); static final maxSessionUsersFactory = UserFactory( - cache: SimpleCache( - storage: UpdatableModelSimpleStorage( - size: UpdatableModelSimpleStorage.MAX_INT))); + cache: + SimpleCache(storage: UpdatableModelSimpleStorage(size: UpdatableModelSimpleStorage.MAX_INT)) + ); factory User.fromJson(Map json, {bool storeInSessionCache = false, bool storeInMaxSessionCache = false}) { @@ -69,17 +69,15 @@ class User extends UpdatableModel { int userId = json['id']; User user = maxSessionUsersFactory.getItemWithIdFromCache(userId) ?? - navigationUsersFactory.getItemWithIdFromCache(userId) ?? - sessionUsersFactory.getItemWithIdFromCache(userId); + navigationUsersFactory.getItemWithIdFromCache(userId) ?? + sessionUsersFactory.getItemWithIdFromCache(userId); if (user != null) { user.update(json); return user; } - return storeInMaxSessionCache - ? maxSessionUsersFactory.fromJson(json) - : storeInSessionCache - ? sessionUsersFactory.fromJson(json) - : navigationUsersFactory.fromJson(json); + return storeInMaxSessionCache ? maxSessionUsersFactory.fromJson(json) : storeInSessionCache + ? sessionUsersFactory.fromJson(json) + : navigationUsersFactory.fromJson(json); } Map toJson() { @@ -98,8 +96,7 @@ class User extends UpdatableModel { 'unread_notifications_count': unreadNotificationsCount, 'posts_count': postsCount, 'invite_count': inviteCount, - 'pending_communities_moderated_objects_count': - pendingCommunitiesModeratedObjectsCount, + 'pending_communities_moderated_objects_count': pendingCommunitiesModeratedObjectsCount, 'active_moderation_penalties_count': activeModerationPenaltiesCount, 'are_guidelines_accepted': areGuidelinesAccepted, 'are_new_post_notifications_enabled': areNewPostNotificationsEnabled, @@ -112,18 +109,10 @@ class User extends UpdatableModel { 'is_fully_connected': isFullyConnected, 'is_pending_connection_confirmation': isPendingConnectionConfirmation, 'is_member_of_communities': isMemberOfCommunities, - 'connected_circles': connectedCircles?.circles - ?.map((Circle circle) => circle.toJson()) - ?.toList(), - 'follow_lists': followLists?.lists - ?.map((FollowsList followList) => followList.toJson()) - ?.toList(), - 'communities_memberships': communitiesMemberships?.communityMemberships - ?.map((CommunityMembership membership) => membership.toJson()) - ?.toList(), - 'communities_invites': communitiesInvites?.communityInvites - ?.map((CommunityInvite invite) => invite.toJson()) - ?.toList(), + 'connected_circles': connectedCircles?.circles?.map((Circle circle) => circle.toJson())?.toList(), + 'follow_lists': followLists?.lists?.map((FollowsList followList) => followList.toJson())?.toList(), + 'communities_memberships': communitiesMemberships?.communityMemberships?.map((CommunityMembership membership) => membership.toJson())?.toList(), + 'communities_invites' : communitiesInvites?.communityInvites?.map((CommunityInvite invite) => invite.toJson())?.toList(), }; } @@ -174,8 +163,7 @@ class User extends UpdatableModel { void updateFromJson(Map json) { if (json.containsKey('username')) username = json['username']; if (json.containsKey('uuid')) uuid = json['uuid']; - if (json.containsKey('date_joined')) - dateJoined = navigationUsersFactory.parseDateJoined(json['date_joined']); + if (json.containsKey('date_joined')) dateJoined = navigationUsersFactory.parseDateJoined(json['date_joined']); if (json.containsKey('are_guidelines_accepted')) areGuidelinesAccepted = json['are_guidelines_accepted']; if (json.containsKey('email')) email = json['email']; @@ -211,9 +199,7 @@ class User extends UpdatableModel { unreadNotificationsCount = json['unread_notifications_count']; if (json.containsKey('posts_count')) postsCount = json['posts_count']; if (json.containsKey('invite_count')) inviteCount = json['invite_count']; - if (json.containsKey('are_new_post_notifications_enabled')) - areNewPostNotificationsEnabled = - json['are_new_post_notifications_enabled']; + if (json.containsKey('are_new_post_notifications_enabled')) areNewPostNotificationsEnabled = json['are_new_post_notifications_enabled']; if (json.containsKey('is_following')) isFollowing = json['is_following']; if (json.containsKey('is_followed')) isFollowed = json['is_followed']; if (json.containsKey('is_connected')) isConnected = json['is_connected']; @@ -424,17 +410,6 @@ class User extends UpdatableModel { notifyUpdate(); } - bool canEnablePostSubscriptionNotifications(Post post) { - User loggedInUser = this; - bool _canDisableOrEnablePostSubscriptionComments = true; - - if (post.getCreatorId() == loggedInUser.id) { - _canDisableOrEnablePostSubscriptionComments = false; - } - - return _canDisableOrEnablePostSubscriptionComments; - } - bool canDisableOrEnableCommentsForPost(Post post) { User loggedInUser = this; bool _canDisableOrEnableComments = false; @@ -611,6 +586,10 @@ class User extends UpdatableModel { return loggedInUser.id != postCommenter.id; } + bool canReplyPostComment(PostComment postComment) { + return postComment.parentComment == null; + } + bool canDeletePostComment(Post post, PostComment postComment) { User loggedInUser = this; User postCommenter = postComment.commenter; @@ -661,8 +640,7 @@ class UserFactory extends UpdatableModelFactory { followingCount: json['following_count'], isFollowing: json['is_following'], isFollowed: json['is_followed'], - areNewPostNotificationsEnabled: - json['are_new_post_notifications_enabled'], + areNewPostNotificationsEnabled: json['are_new_post_notifications_enabled'], isConnected: json['is_connected'], isGlobalModerator: json['is_global_moderator'], isBlocked: json['is_blocked'], diff --git a/lib/models/user_notifications_settings.dart b/lib/models/user_notifications_settings.dart index 0373b5b07..7ddda71c5 100644 --- a/lib/models/user_notifications_settings.dart +++ b/lib/models/user_notifications_settings.dart @@ -1,6 +1,10 @@ class UserNotificationsSettings { final int id; - bool postNotifications; + bool postCommentNotifications; + bool postCommentReactionNotifications; + bool postCommentReplyNotifications; + bool postCommentUserMentionNotifications; + bool postUserMentionNotifications; bool postReactionNotifications; bool followNotifications; bool connectionRequestNotifications; @@ -14,7 +18,12 @@ class UserNotificationsSettings { this.connectionConfirmedNotifications, this.connectionRequestNotifications, this.followNotifications, - this.postNotifications, + this.postCommentNotifications, + this.postCommentReactionNotifications, + this.postCommentUserMentionNotifications, + this.postUserMentionNotifications, + this.postCommentReplyNotifications, + this.postReactionNotifications, this.communityInviteNotifications, this.communityNewPostNotifications, this.userNewPostNotifications}); @@ -27,12 +36,22 @@ class UserNotificationsSettings { connectionRequestNotifications: parsedJson['connection_request_notifications'], followNotifications: parsedJson['follow_notifications'], - postNotifications: parsedJson['post_notifications'], + postCommentNotifications: parsedJson['post_comment_notifications'], + postCommentReactionNotifications: + parsedJson['post_comment_reaction_notifications'], + postCommentReplyNotifications: + parsedJson['post_comment_reply_notifications'], + postCommentUserMentionNotifications: + parsedJson['post_comment_user_mention_notifications'], + postUserMentionNotifications: + parsedJson['post_user_mention_notifications'], + postReactionNotifications: parsedJson['post_reaction_notifications'], communityInviteNotifications: parsedJson['community_invite_notifications'], communityNewPostNotifications: parsedJson['community_new_post_notifications'], - userNewPostNotifications: parsedJson['user_new_post_notifications'], + userNewPostNotifications: + parsedJson['user_new_post_notifications'], ); } @@ -42,7 +61,12 @@ class UserNotificationsSettings { 'connections_confirmed_notifications': connectionConfirmedNotifications, 'connection_request_notifications': connectionRequestNotifications, 'follow_notifications': followNotifications, - 'post_notifications': postNotifications, + 'post_comment_notifications': postCommentNotifications, + 'post_comment_reaction_notifications': postCommentReactionNotifications, + 'post_comment_user_mention_notifications': postCommentUserMentionNotifications, + 'post_user_mention_notifications': postUserMentionNotifications, + 'post_comment_reply_notifications': postCommentReplyNotifications, + 'post_reaction_notifications': postReactionNotifications, 'community_invite_notifications': communityInviteNotifications, 'community_new_post_notifications': communityNewPostNotifications, 'user_new_post_notifications': userNewPostNotifications, @@ -57,8 +81,24 @@ class UserNotificationsSettings { connectionRequestNotifications = json['connection_request_notifications']; if (json.containsKey('follow_notifications')) followNotifications = json['follow_notifications']; - if (json.containsKey('post_notifications')) - postNotifications = json['post_notifications']; + if (json.containsKey('post_comment_notifications')) + postCommentNotifications = json['post_comment_notifications']; + + if (json.containsKey('post_comment_reaction_notifications')) + postCommentReactionNotifications = + json['post_comment_reaction_notifications']; + + if (json.containsKey('post_comment_reply_notifications')) + postCommentReplyNotifications = json['post_comment_reply_notifications']; + + if (json.containsKey('post_comment_user_mention_notifications')) + postCommentUserMentionNotifications = json['post_comment_user_mention_notifications']; + + if (json.containsKey('post_user_mention_notifications')) + postUserMentionNotifications = json['post_user_mention_notifications']; + + if (json.containsKey('post_reaction_notifications')) + postReactionNotifications = json['post_reaction_notifications']; if (json.containsKey('community_invite_notifications')) communityInviteNotifications = json['community_invite_notifications']; if (json.containsKey('community_new_post_notifications')) diff --git a/lib/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart b/lib/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart deleted file mode 100644 index 51baa30dc..000000000 --- a/lib/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart +++ /dev/null @@ -1,224 +0,0 @@ -import 'package:Okuna/models/post.dart'; -import 'package:Okuna/models/post_comment.dart'; -import 'package:Okuna/models/post_comment_notifications_subscription.dart'; -import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart'; -import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/provider.dart'; -import 'package:Okuna/services/toast.dart'; -import 'package:Okuna/services/user.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:async/async.dart'; - -import '../rounded_bottom_sheet.dart'; - -class OBManagePostCommentNotificationsBottomSheet extends StatefulWidget { - final Post post; - final PostComment postComment; - final OnNotificationSettingsSave onNotificationSettingsSave; - - OBManagePostCommentNotificationsBottomSheet( - {Key key, - @required this.post, - @required this.postComment, - this.onNotificationSettingsSave}) - : super(key: key); - - @override - OBManagePostCommentNotificationsBottomSheetState createState() { - return OBManagePostCommentNotificationsBottomSheetState(); - } -} - -class OBManagePostCommentNotificationsBottomSheetState - extends State { - UserService _userService; - LocalizationService _localizationService; - ToastService _toastService; - PostCommentNotificationsSubscription postCommentNotificationsSubscription; - CancelableOperation _saveOperation; - CancelableOperation _muteOperation; - - @override - void initState() { - super.initState(); - postCommentNotificationsSubscription = - widget.postComment.postCommentNotificationsSubscription; - } - - @override - Widget build(BuildContext context) { - var openbookProvider = OpenbookProvider.of(context); - _userService = openbookProvider.userService; - _localizationService = openbookProvider.localizationService; - _toastService = openbookProvider.toastService; - - return OBRoundedBottomSheet( - child: OBManageNotifications( - notificationSettings: _getNotificationSettingsList(), - onWantsToSaveSettings: _onWantsToSaveSettings, - mutePostLabelText: - _localizationService.post__mute_post_comment_notifications_text, - unmutePostLabelText: _localizationService - .post__unmute_post_comment_notifications_text, - isMuted: widget.postComment.isMuted, - onWantsToToggleMute: _onWantsToToggleMute)); - } - - @override - void dispose() { - super.dispose(); - if (_saveOperation != null) _saveOperation.cancel(); - if (_muteOperation != null) _muteOperation.cancel(); - } - - List _getNotificationSettingsList() { - List _notificationSettings = [ - PostCommentNotificationSetting( - key: PostCommentNotificationSetting.REPLY_NOTIFICATIONS, - value: - postCommentNotificationsSubscription?.replyNotifications ?? false, - isDisabled: widget.postComment.isMuted, - localizedTitle: - _localizationService.post__manage_notifications_replies_title, - localizedDesc: _localizationService - .post__manage_notifications_replies_post_comment_desc), - ]; - // add post reaction setting if user is creator - if (widget.postComment.commenter == _userService.getLoggedInUser()) { - _notificationSettings.insert( - 0, - PostCommentNotificationSetting( - key: PostCommentNotificationSetting.REACTION_NOTIFICATIONS, - value: - postCommentNotificationsSubscription?.reactionNotifications ?? - false, - isDisabled: widget.postComment.isMuted, - localizedTitle: _localizationService - .post__manage_notifications_reactions_title, - localizedDesc: _localizationService - .post__manage_notifications_reactions_post_comment_desc)); - } - return _notificationSettings; - } - - void _onWantsToToggleMute() async { - try { - if (widget.postComment.isMuted) { - _muteOperation = CancelableOperation.fromFuture( - _userService.unmutePostComment( - post: widget.post, postComment: widget.postComment)); - await _muteOperation.value; - _toastService.success( - message: _localizationService - .post__manage_notifications_successfully_unmuted, - context: context); - } else { - _muteOperation = CancelableOperation.fromFuture( - _userService.mutePostComment( - post: widget.post, postComment: widget.postComment)); - await _muteOperation.value; - _toastService.success( - message: _localizationService - .post__manage_notifications_successfully_muted, - context: context); - } - } catch (error) { - _onError(error); - } - } - - void _onWantsToSaveSettings( - Map notificationSettingsMap) async { - try { - if (widget.postComment.postCommentNotificationsSubscription == null) { - _saveOperation = CancelableOperation.fromFuture( - createUserPostCommentNotificationsSubscription( - notificationSettingsMap)); - await _saveOperation.value; - } else { - _saveOperation = CancelableOperation.fromFuture( - _updateUserPostCommentNotificationsSubscription( - notificationSettingsMap)); - await _saveOperation.value; - } - } catch (error) { - _onError(error); - } - if (widget.onNotificationSettingsSave != null) - widget.onNotificationSettingsSave(); - } - - void _onError(error) async { - if (error is HttpieConnectionRefusedError) { - _toastService.error( - message: error.toHumanReadableMessage(), context: context); - } else if (error is HttpieRequestError) { - String errorMessage = await error.toHumanReadableMessage(); - _toastService.error(message: errorMessage, context: context); - } else { - _toastService.error( - message: _localizationService.trans('error__unknown_error'), - context: context); - throw error; - } - } - - Future createUserPostCommentNotificationsSubscription( - Map notificationSettingsMap) { - return _userService.createPostCommentNotificationsSubscription( - post: widget.post, - postComment: widget.postComment, - reactionNotifications: notificationSettingsMap[ - PostCommentNotificationSetting.REACTION_NOTIFICATIONS], - replyNotifications: notificationSettingsMap[ - PostCommentNotificationSetting.REPLY_NOTIFICATIONS], - ); - } - - Future _updateUserPostCommentNotificationsSubscription( - Map notificationSettingsMap) { - return _userService.updatePostCommentNotificationsSubscription( - post: widget.post, - postComment: widget.postComment, - reactionNotifications: notificationSettingsMap[ - PostCommentNotificationSetting.REACTION_NOTIFICATIONS], - replyNotifications: notificationSettingsMap[ - PostCommentNotificationSetting.REPLY_NOTIFICATIONS], - ); - } -} - -class PostCommentNotificationSetting extends NotificationSetting { - String key; - bool value; - bool isDisabled; - String localizedTitle; - String localizedDesc; - - static const REPLY_NOTIFICATIONS = 'replyNotifications'; - static const REACTION_NOTIFICATIONS = 'reactionNotifications'; - List _notificationTypes = [ - REPLY_NOTIFICATIONS, - REACTION_NOTIFICATIONS - ]; - - PostCommentNotificationSetting( - {this.key, - this.value, - this.isDisabled, - this.localizedTitle, - this.localizedDesc}) { - if (!_notificationTypes.contains(this.key)) - throw PostCommentNotificationSettingKeyError(this.key); - } -} - -class PostCommentNotificationSettingKeyError extends Error { - final String message; - PostCommentNotificationSettingKeyError(this.message); - String toString() => - "Unsupported key for post comment notification setting: $message"; -} - -typedef void OnNotificationSettingsSave(); diff --git a/lib/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart b/lib/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart deleted file mode 100644 index 4a7017d6c..000000000 --- a/lib/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart +++ /dev/null @@ -1,224 +0,0 @@ -import 'package:Okuna/models/post.dart'; -import 'package:Okuna/models/post_notifications_subscription.dart'; -import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart'; -import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/provider.dart'; -import 'package:Okuna/services/toast.dart'; -import 'package:Okuna/services/user.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:async/async.dart'; - -import '../rounded_bottom_sheet.dart'; - -class OBManagePostNotificationsBottomSheet extends StatefulWidget { - final Post post; - final OnNotificationSettingsSave onNotificationSettingsSave; - - OBManagePostNotificationsBottomSheet( - {Key key, this.onNotificationSettingsSave, this.post}) - : super(key: key); - - @override - OBManagePostNotificationsBottomSheetState createState() { - return OBManagePostNotificationsBottomSheetState(); - } -} - -class OBManagePostNotificationsBottomSheetState - extends State { - UserService _userService; - LocalizationService _localizationService; - ToastService _toastService; - PostNotificationsSubscription _postNotificationsSubscription; - CancelableOperation _saveOperation; - CancelableOperation _muteOperation; - - @override - void initState() { - super.initState(); - _postNotificationsSubscription = widget.post.postNotificationsSubscription; - } - - @override - Widget build(BuildContext context) { - var openbookProvider = OpenbookProvider.of(context); - _userService = openbookProvider.userService; - _localizationService = openbookProvider.localizationService; - _toastService = openbookProvider.toastService; - - return OBRoundedBottomSheet( - child: OBManageNotifications( - notificationSettings: _getNotificationSettingsList(), - onWantsToSaveSettings: _onWantsToSaveSettings, - isMuted: widget.post.isMuted, - mutePostLabelText: - _localizationService.post__mute_post_notifications_text, - unmutePostLabelText: - _localizationService.post__unmute_post_notifications_text, - onWantsToToggleMute: _onWantsToToggleMute)); - } - - @override - void dispose() { - super.dispose(); - if (_saveOperation != null) _saveOperation.cancel(); - if (_muteOperation != null) _muteOperation.cancel(); - } - - List _getNotificationSettingsList() { - List _notificationSettings = [ - PostNotificationSetting( - key: PostNotificationSetting.COMMENT_NOTIFICATIONS, - value: _postNotificationsSubscription?.commentNotifications ?? false, - isDisabled: widget.post.isMuted, - localizedTitle: - _localizationService.post__manage_notifications_comments_title, - localizedDesc: - _localizationService.post__manage_notifications_comments_desc), - PostNotificationSetting( - key: PostNotificationSetting.REPLY_NOTIFICATIONS, - value: _postNotificationsSubscription?.replyNotifications ?? false, - isDisabled: widget.post.isMuted, - localizedTitle: - _localizationService.post__manage_notifications_replies_title, - localizedDesc: - _localizationService.post__manage_notifications_replies_desc), - ]; - // add post reaction setting if user is creator - if (widget.post.creator == _userService.getLoggedInUser()) { - _notificationSettings.insert( - 0, - PostNotificationSetting( - key: PostNotificationSetting.REACTION_NOTIFICATIONS, - value: _postNotificationsSubscription?.reactionNotifications ?? - false, - isDisabled: widget.post.isMuted, - localizedTitle: _localizationService - .post__manage_notifications_reactions_title, - localizedDesc: _localizationService - .post__manage_notifications_reactions_desc)); - } - return _notificationSettings; - } - - void _onWantsToToggleMute() async { - try { - if (widget.post.isMuted) { - _muteOperation = CancelableOperation.fromFuture( - _userService.unmutePost(widget.post)); - await _muteOperation.value; - _toastService.success( - message: _localizationService - .post__manage_notifications_successfully_unmuted, - context: context); - } else { - _muteOperation = - CancelableOperation.fromFuture(_userService.mutePost(widget.post)); - await _muteOperation.value; - _toastService.success( - message: _localizationService - .post__manage_notifications_successfully_muted, - context: context); - } - } catch (error) { - _onError(error); - } - } - - void _onWantsToSaveSettings( - Map notificationSettingsMap) async { - try { - if (widget.post.postNotificationsSubscription == null) { - _saveOperation = CancelableOperation.fromFuture( - _createUserPostNotificationsSubscription(notificationSettingsMap)); - await _saveOperation.value; - } else { - _saveOperation = CancelableOperation.fromFuture( - _updateUserPostNotificationsSubscription(notificationSettingsMap)); - await _saveOperation.value; - } - } catch (error) { - _onError(error); - } - if (widget.onNotificationSettingsSave != null) - widget.onNotificationSettingsSave(); - } - - void _onError(error) async { - if (error is HttpieConnectionRefusedError) { - _toastService.error( - message: error.toHumanReadableMessage(), context: context); - } else if (error is HttpieRequestError) { - String errorMessage = await error.toHumanReadableMessage(); - _toastService.error(message: errorMessage, context: context); - } else { - _toastService.error( - message: _localizationService.trans('error__unknown_error'), - context: context); - throw error; - } - } - - Future _createUserPostNotificationsSubscription( - Map notificationSettingsMap) { - return _userService.createPostNotificationsSubscription( - post: widget.post, - commentNotifications: notificationSettingsMap[ - PostNotificationSetting.COMMENT_NOTIFICATIONS], - reactionNotifications: notificationSettingsMap[ - PostNotificationSetting.REACTION_NOTIFICATIONS], - replyNotifications: - notificationSettingsMap[PostNotificationSetting.REPLY_NOTIFICATIONS], - ); - } - - Future _updateUserPostNotificationsSubscription( - Map notificationSettingsMap) { - return _userService.updatePostNotificationsSubscription( - post: widget.post, - commentNotifications: notificationSettingsMap[ - PostNotificationSetting.COMMENT_NOTIFICATIONS], - reactionNotifications: notificationSettingsMap[ - PostNotificationSetting.REACTION_NOTIFICATIONS], - replyNotifications: - notificationSettingsMap[PostNotificationSetting.REPLY_NOTIFICATIONS], - ); - } -} - -class PostNotificationSetting extends NotificationSetting { - String key; - bool value; - bool isDisabled; - String localizedTitle; - String localizedDesc; - - static const COMMENT_NOTIFICATIONS = 'commentNotifications'; - static const REPLY_NOTIFICATIONS = 'replyNotifications'; - static const REACTION_NOTIFICATIONS = 'reactionNotifications'; - List _notificationTypes = [ - COMMENT_NOTIFICATIONS, - REPLY_NOTIFICATIONS, - REACTION_NOTIFICATIONS - ]; - - PostNotificationSetting( - {this.key, - this.value, - this.isDisabled, - this.localizedTitle, - this.localizedDesc}) { - if (!_notificationTypes.contains(this.key)) - throw PostNotificationSettingKeyError(this.key); - } -} - -class PostNotificationSettingKeyError extends Error { - final String message; - PostNotificationSettingKeyError(this.message); - String toString() => - "Unsupported key for post notification setting: $message"; -} - -typedef void OnNotificationSettingsSave(); diff --git a/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart b/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart deleted file mode 100644 index 03c2b6231..000000000 --- a/lib/pages/home/bottom_sheets/manage_notifications/widgets/manage_notifications.dart +++ /dev/null @@ -1,162 +0,0 @@ -import 'package:Okuna/widgets/fields/toggle_field.dart'; -import 'package:Okuna/widgets/buttons/button.dart'; -import 'package:Okuna/widgets/theming/text.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class OBManageNotifications extends StatefulWidget { - List notificationSettings; - OnWantsToSaveSettings onWantsToSaveSettings; - String mutePostLabelText; - String unmutePostLabelText; - VoidCallback onWantsToToggleMute; - bool isMuted; - - OBManageNotifications( - {Key key, - @required this.onWantsToSaveSettings, - @required this.notificationSettings, - @required this.mutePostLabelText, - @required this.unmutePostLabelText, - @required this.onWantsToToggleMute, - @required this.isMuted}) - : super(key: key); - - @override - OBManageNotificationsState createState() { - return OBManageNotificationsState(); - } -} - -class OBManageNotificationsState extends State { - bool _requestInProgress; - bool _isMuted; - - @override - void initState() { - super.initState(); - _requestInProgress = false; - _isMuted = widget.isMuted; - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.symmetric(vertical: 15.0), - child: Opacity( - opacity: _requestInProgress ? 0.5 : 1, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildNotificationSettings(), - ))); - } - - void _toggleNotificationSetting(NotificationSetting setting) { - setState(() { - setting.value = !setting.value; - }); - } - - void _toggleMute() { - setState(() { - _isMuted = !_isMuted; - }); - } - - List _buildNotificationSettings() { - List _settings = []; - - widget.notificationSettings - .forEach((NotificationSetting notificationSetting) => { - _settings.add(OBToggleField( - key: Key(notificationSetting.key), - value: notificationSetting.value, - isDisabled: notificationSetting.isDisabled, - title: notificationSetting.localizedTitle, - subtitle: OBText(notificationSetting.localizedDesc), - onChanged: (bool newValue) { - _toggleNotificationSetting(notificationSetting); - _onWantsToSaveSettings(); - }, - onTap: () { - _toggleNotificationSetting(notificationSetting); - _onWantsToSaveSettings(); - }, // toggle - hasDivider: false, - )) - }); - - _settings.add(_buildMuteButtonRow()); - - return _settings; - } - - Widget _buildMuteButtonRow() { - return Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20), - child: Row( - children: [ - Expanded( - child: OBButton( - size: OBButtonSize.large, - type: OBButtonType.highlight, - child: _isMuted - ? Text(widget.unmutePostLabelText) - : Text(widget.mutePostLabelText), - onPressed: _onWantsToToggleMute, - ), - ), - ], - ), - ); - } - - void _onWantsToSaveSettings() { - _setRequestInProgress(true); - Map _notificationSettingsMap = - _getNotificationSettingsMap(widget.notificationSettings); - widget.onWantsToSaveSettings(_notificationSettingsMap); - _setRequestInProgress(false); - } - - Map _getNotificationSettingsMap( - List notificationSettings) { - Map _map = {}; - notificationSettings.forEach((NotificationSetting setting) { - _map[setting.key] = setting.value; - }); - return _map; - } - - void _onWantsToToggleMute() async { - try { - widget.onWantsToToggleMute(); - _toggleMute(); - } catch (error) { - print('Manage Notifications Error while toggling mute'); - } - - setState(() { - widget.notificationSettings.forEach((NotificationSetting setting) => - setting.isDisabled = !setting.isDisabled); - }); - } - - void _setRequestInProgress(bool requestInProgress) { - setState(() { - _requestInProgress = requestInProgress; - }); - } -} - -abstract class NotificationSetting { - String key; - bool value; - bool isDisabled; - String localizedTitle; - String localizedDesc; -} - -typedef void OnWantsToSaveSettings( - Map notificationSettingsMap); diff --git a/lib/pages/home/bottom_sheets/post_actions.dart b/lib/pages/home/bottom_sheets/post_actions.dart index e9bcdcf73..16d0e9ccf 100644 --- a/lib/pages/home/bottom_sheets/post_actions.dart +++ b/lib/pages/home/bottom_sheets/post_actions.dart @@ -13,9 +13,10 @@ import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/post/post.dart'; import 'package:Okuna/widgets/theming/text.dart'; import 'package:Okuna/widgets/tiles/actions/close_post_tile.dart'; +import 'package:Okuna/widgets/tiles/actions/disable_comments_post_tile.dart'; import 'package:Okuna/widgets/tiles/actions/exclude_community_from_profile_posts_tile.dart'; import 'package:Okuna/widgets/tiles/actions/exclude_community_from_top_posts_tile.dart'; -import 'package:Okuna/widgets/tiles/actions/manage_post_notifications_tile.dart'; +import 'package:Okuna/widgets/tiles/actions/mute_post_tile.dart'; import 'package:Okuna/widgets/tiles/actions/report_post_tile.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -95,11 +96,20 @@ class OBPostActionsBottomSheetState extends State { widget.onPostCommunityExcludedFromProfilePosts)); } - postActions.add(OBManagePostNotificationsTile( + postActions.add(OBMutePostTile( post: post, - onNotificationSettingsSave: _dismiss, + onMutedPost: _dismiss, + onUnmutedPost: _dismiss, )); + if (loggedInUser.canDisableOrEnableCommentsForPost(post)) { + postActions.add(OBDisableCommentsPostTile( + post: post, + onDisableComments: _dismiss, + onEnableComments: _dismiss, + )); + } + if (loggedInUser.canCloseOrOpenPost(post)) { postActions.add(OBClosePostTile( post: post, diff --git a/lib/pages/home/bottom_sheets/post_comment_more_actions.dart b/lib/pages/home/bottom_sheets/post_comment_more_actions.dart index b0d05a0f0..2e531956d 100644 --- a/lib/pages/home/bottom_sheets/post_comment_more_actions.dart +++ b/lib/pages/home/bottom_sheets/post_comment_more_actions.dart @@ -11,7 +11,6 @@ import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/user.dart'; import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/theming/text.dart'; -import 'package:Okuna/widgets/tiles/actions/manage_post_comment_notifications_tile.dart'; import 'package:Okuna/widgets/tiles/actions/mute_post_comment_tile.dart'; import 'package:async/async.dart'; import 'package:flutter/material.dart'; @@ -82,10 +81,9 @@ class OBPostCommentMoreActionsBottomSheetState List _buildActionTiles() { List actionTiles = [ - OBManagePostCommentNotificationsTile( - post: widget.post, + OBMutePostCommentTile( postComment: widget.postComment, - onNotificationSettingsSave: _dismissMoreActions, + post: widget.post, ) ]; User loggedInUser = _userService.getLoggedInUser(); diff --git a/lib/pages/home/modals/post_comment/post_comment_reply_expanded.dart b/lib/pages/home/modals/post_comment/post_comment_reply_expanded.dart index 311ea99ea..c734d8d08 100644 --- a/lib/pages/home/modals/post_comment/post_comment_reply_expanded.dart +++ b/lib/pages/home/modals/post_comment/post_comment_reply_expanded.dart @@ -26,7 +26,6 @@ import 'package:flutter/material.dart'; class OBPostCommentReplyExpandedModal extends StatefulWidget { final Post post; final PostComment postComment; - final PostComment postCommentReply; final Function(PostComment) onReplyAdded; final Function(PostComment) onReplyDeleted; @@ -34,7 +33,6 @@ class OBPostCommentReplyExpandedModal extends StatefulWidget { {Key key, this.post, this.postComment, - this.postCommentReply, this.onReplyAdded, this.onReplyDeleted}) : super(key: key); @@ -83,20 +81,15 @@ class OBPostCommentReplyExpandedModalState @override void bootstrap() { super.bootstrap(); - String defaultControllerText = widget.postCommentReply != null - ? '@${widget.postCommentReply.commenter.username} ' - : null; _textController = DraftTextEditingController.comment(widget.post.id, commentId: widget.postComment != null ? widget.postComment.id : null, - text: defaultControllerText, draftService: _draftService); _textController.addListener(_onPostCommentTextChanged); setAutocompleteTextController(_textController); String hintText = _localizationService.post__comment_reply_expanded_reply_hint_text; - _postCommentItemsWidgets = [ OBCreatePostText(controller: _textController, hintText: hintText) ]; @@ -202,7 +195,7 @@ class OBPostCommentReplyExpandedModalState children: [ OBPostComment( post: widget.post, - postComment: widget.postCommentReply ?? widget.postComment, + postComment: widget.postComment, showActions: false, showReactions: false, showReplies: false, diff --git a/lib/pages/home/pages/notifications/pages/notifications_settings.dart b/lib/pages/home/pages/notifications/pages/notifications_settings.dart index ca6f489de..14cb99b3a 100644 --- a/lib/pages/home/pages/notifications/pages/notifications_settings.dart +++ b/lib/pages/home/pages/notifications/pages/notifications_settings.dart @@ -35,7 +35,12 @@ class OBNotificationsSettingsPageState bool _pushNotifications; - bool _postNotifications; + bool _postCommentNotifications; + bool _postCommentReactionNotifications; + bool _postCommentReplyNotifications; + bool _postCommentUserMentionNotifications; + bool _postUserMentionNotifications; + bool _postReactionNotifications; bool _followNotifications; bool _connectionRequestNotifications; bool _communityInviteNotifications; @@ -47,12 +52,17 @@ class OBNotificationsSettingsPageState super.initState(); _needsBootstrap = true; _bootstrapInProgress = true; - _postNotifications = true; + _postCommentNotifications = true; + _postCommentReactionNotifications = true; + _postCommentReplyNotifications = true; + _postReactionNotifications = true; _followNotifications = true; _connectionRequestNotifications = true; _communityInviteNotifications = true; _communityNewPostNotifications = true; _userNewPostNotifications = true; + _postUserMentionNotifications = true; + _postCommentUserMentionNotifications = true; } @override @@ -140,14 +150,68 @@ class OBNotificationsSettingsPageState ), listItemSeparator, OBToggleField( - key: Key('Post notifications'), - value: _postNotifications, - title: _localizationService.notifications__post_notifications_title, + key: Key('Post comments'), + value: _postCommentNotifications, + title: _localizationService.trans('notifications__comment_title'), + subtitle: + OBText(_localizationService.trans('notifications__comment_desc')), + onChanged: _setPostCommentNotifications, + onTap: _togglePostCommentNotifications, + hasDivider: false, + ), + OBToggleField( + key: Key('Post comments replies'), + value: _postCommentReplyNotifications, + title: + _localizationService.trans('notifications__comment_reply_title'), subtitle: OBText( - _localizationService.notifications__post_notifications_desc, + _localizationService.trans('notifications__comment_reply_desc')), + onChanged: _setPostCommentReplyNotifications, + onTap: _togglePostCommentReplyNotifications, + hasDivider: false, + ), + OBToggleField( + key: Key('Post comment user mentions'), + value: _postCommentUserMentionNotifications, + title: _localizationService.notifications__comment_user_mention_title, + subtitle: OBText( + _localizationService.notifications__comment_user_mention_desc), + onChanged: _setPostCommentUserMentionNotifications, + onTap: _togglePostCommentUserMentionNotifications, + hasDivider: false, + ), + OBToggleField( + key: Key('Post user mentions'), + value: _postUserMentionNotifications, + title: _localizationService.notifications__post_user_mention_title, + subtitle: OBText( + _localizationService.notifications__post_user_mention_desc), + onChanged: _setPostUserMentionNotifications, + onTap: _togglePostUserMentionNotifications, + hasDivider: false, + ), + OBToggleField( + key: Key('Post comment reactions'), + value: _postCommentReactionNotifications, + title: _localizationService + .trans('notifications__comment_reaction_title'), + subtitle: OBText(_localizationService + .trans('notifications__comment_reaction_desc')), + onChanged: _setPostCommentReactionNotifications, + onTap: _togglePostCommentReactionNotifications, + hasDivider: false, + ), + listItemSeparator, + OBToggleField( + key: Key('Post reaction'), + value: _postReactionNotifications, + title: + _localizationService.trans('notifications__post_reaction_title'), + subtitle: OBText( + _localizationService.trans('notifications__post_reaction_desc'), ), - onChanged: _setPostNotifications, - onTap: _togglePostNotifications, + onChanged: _setPostReactionNotifications, + onTap: _togglePostReactionNotifications, hasDivider: false, ), OBToggleField( @@ -165,8 +229,8 @@ class OBNotificationsSettingsPageState key: Key('Community new post'), value: _communityNewPostNotifications, title: _localizationService.notifications__community_new_post_title, - subtitle: OBText( - _localizationService.notifications__community_new_post_desc), + subtitle: OBText(_localizationService + .notifications__community_new_post_desc), onChanged: _setCommunityNewPostNotifications, onTap: _toggleCommunityNewPostNotifications, hasDivider: false, @@ -175,8 +239,8 @@ class OBNotificationsSettingsPageState key: Key('User new post'), value: _userNewPostNotifications, title: _localizationService.notifications__user_new_post_title, - subtitle: - OBText(_localizationService.notifications__user_new_post_desc), + subtitle: OBText(_localizationService + .notifications__user_new_post_desc), onChanged: _setUserNewPostNotifications, onTap: _toggleUserNewPostNotifications, hasDivider: false, @@ -268,13 +332,74 @@ class OBNotificationsSettingsPageState _submitNotificationsSettings(); } - void _togglePostNotifications() { - _setPostNotifications(!_postNotifications); + void _togglePostCommentNotifications() { + _setPostCommentNotifications(!_postCommentNotifications); + } + + void _setPostCommentNotifications(bool newValue) { + setState(() { + _postCommentNotifications = newValue; + }); + + _submitNotificationsSettings(); + } + + void _togglePostCommentReactionNotifications() { + _setPostCommentReactionNotifications(!_postCommentReactionNotifications); + } + + void _setPostCommentReactionNotifications(bool newValue) { + setState(() { + _postCommentReactionNotifications = newValue; + }); + + _submitNotificationsSettings(); + } + + void _togglePostCommentReplyNotifications() { + _setPostCommentReplyNotifications(!_postCommentReplyNotifications); + } + + void _setPostCommentReplyNotifications(bool newValue) { + setState(() { + _postCommentReplyNotifications = newValue; + }); + + _submitNotificationsSettings(); + } + + void _togglePostCommentUserMentionNotifications() { + _setPostCommentUserMentionNotifications( + !_postCommentUserMentionNotifications); + } + + void _setPostCommentUserMentionNotifications(bool newValue) { + setState(() { + _postCommentUserMentionNotifications = newValue; + }); + + _submitNotificationsSettings(); + } + + void _togglePostUserMentionNotifications() { + _setPostUserMentionNotifications(!_postUserMentionNotifications); + } + + void _setPostUserMentionNotifications(bool newValue) { + setState(() { + _postUserMentionNotifications = newValue; + }); + + _submitNotificationsSettings(); + } + + void _togglePostReactionNotifications() { + _setPostReactionNotifications(!_postReactionNotifications); } - void _setPostNotifications(bool newValue) { + void _setPostReactionNotifications(bool newValue) { setState(() { - _postNotifications = newValue; + _postReactionNotifications = newValue; }); _submitNotificationsSettings(); @@ -312,7 +437,12 @@ class OBNotificationsSettingsPageState try { _userService.updateAuthenticatedUserNotificationsSettings( followNotifications: _followNotifications, - postNotifications: _postNotifications, + postCommentNotifications: _postCommentNotifications, + postCommentReplyNotifications: _postCommentReplyNotifications, + postCommentUserMentionNotifications: _postCommentUserMentionNotifications, + postUserMentionNotifications: _postUserMentionNotifications, + postCommentReactionNotifications: _postCommentReactionNotifications, + postReactionNotifications: _postReactionNotifications, connectionRequestNotifications: _connectionRequestNotifications, communityNewPostNotifications: _communityNewPostNotifications, userNewPostNotifications: _userNewPostNotifications, @@ -342,13 +472,24 @@ class OBNotificationsSettingsPageState setState(() { _connectionRequestNotifications = notificationSettings.connectionRequestNotifications; - _postNotifications = notificationSettings.postNotifications; + _postCommentNotifications = notificationSettings.postCommentNotifications; + _postCommentReactionNotifications = + notificationSettings.postCommentReactionNotifications; + _postCommentUserMentionNotifications = + notificationSettings.postCommentUserMentionNotifications; + _postUserMentionNotifications = + notificationSettings.postUserMentionNotifications; + _postCommentReplyNotifications = + notificationSettings.postCommentReplyNotifications; + _postReactionNotifications = + notificationSettings.postReactionNotifications; _followNotifications = notificationSettings.followNotifications; _communityInviteNotifications = notificationSettings.communityInviteNotifications; _communityNewPostNotifications = notificationSettings.communityNewPostNotifications; - _userNewPostNotifications = notificationSettings.userNewPostNotifications; + _userNewPostNotifications = + notificationSettings.userNewPostNotifications; }); } diff --git a/lib/pages/home/pages/post_comments/widgets/post_comment/widgets/post_comment_actions.dart b/lib/pages/home/pages/post_comments/widgets/post_comment/widgets/post_comment_actions.dart index 6ff201fbf..df788bc9d 100644 --- a/lib/pages/home/pages/post_comments/widgets/post_comment/widgets/post_comment_actions.dart +++ b/lib/pages/home/pages/post_comments/widgets/post_comment/widgets/post_comment_actions.dart @@ -84,7 +84,10 @@ class OBPostCommentActionsState extends State { _buildReactButton(), ]; - if (widget.showReplyAction) { + if (widget.showReplyAction && + _userService + .getLoggedInUser() + .canReplyPostComment(widget.postComment)) { actionItems.add(_buildReplyButton()); } @@ -151,6 +154,7 @@ class OBPostCommentActionsState extends State { } Widget _buildReplyButton() { + return Padding( padding: const EdgeInsets.only(top: 10, bottom: 10, right: 10), child: GestureDetector( @@ -174,17 +178,13 @@ class OBPostCommentActionsState extends State { PostComment comment = await _modalService.openExpandedReplyCommenter( context: context, post: widget.post, - postComment: widget.postComment.parentComment ?? - widget.postComment, // if reply to reply use parent comment - postCommentReply: widget.postComment.parentComment != null - ? widget.postComment - : null, + postComment: widget.postComment, onReplyDeleted: widget.onReplyDeleted, onReplyAdded: widget.onReplyAdded); if (comment != null) { await _navigationService.navigateToPostCommentReplies( post: widget.post, - postComment: widget.postComment.parentComment ?? widget.postComment, + postComment: widget.postComment, onReplyAdded: widget.onReplyAdded, onReplyDeleted: widget.onReplyDeleted, context: context); @@ -244,8 +244,7 @@ class OBPostCommentActionsState extends State { String errorMessage = await error.toHumanReadableMessage(); _toastService.error(message: errorMessage, context: context); } else { - _toastService.error( - message: _localizationService.error__unknown_error, context: context); + _toastService.error(message: _localizationService.error__unknown_error, context: context); throw error; } } diff --git a/lib/services/auth_api.dart b/lib/services/auth_api.dart index 3e857bb70..5217bc22a 100644 --- a/lib/services/auth_api.dart +++ b/lib/services/auth_api.dart @@ -30,8 +30,7 @@ class AuthApiService { static const SEARCH_LINKED_USERS_PATH = 'api/auth/linked-users/search/'; static const GET_BLOCKED_USERS_PATH = 'api/auth/blocked-users/'; static const SEARCH_BLOCKED_USERS_PATH = 'api/auth/blocked-users/search/'; - static const ENABLE_NEW_POST_NOTIFICATIONS_FOR_USER_PATH = - 'api/auth/users/{userUsername}/notifications/subscribe/new-post/'; + static const ENABLE_NEW_POST_NOTIFICATIONS_FOR_USER_PATH = 'api/auth/users/{userUsername}/notifications/subscribe/new-post/'; static const BLOCK_USER_PATH = 'api/auth/users/{userUsername}/block/'; static const UNBLOCK_USER_PATH = 'api/auth/users/{userUsername}/unblock/'; static const GET_FOLLOWERS_PATH = 'api/auth/followers/'; @@ -168,10 +167,12 @@ class AuthApiService { body: body); } - Future verifyRegisterToken({@required String token}) { + Future verifyRegisterToken( + {@required String token}) { Map body = {'token': token}; - return _httpService.post('$apiURL$VERIFY_REGISTER_TOKEN', body: body); + return _httpService.post('$apiURL$VERIFY_REGISTER_TOKEN', + body: body); } Future getUserWithAuthToken(String authToken) { @@ -266,20 +267,14 @@ class AuthApiService { return _httpService.post(_makeApiUrl(path), appendAuthorizationToken: true); } - Future enableNewPostNotificationsForUserWithUsername( - String userUsername) { - String path = - _makeEnableNewPostNotificationsForUserWithUsernamePath(userUsername); - return _httpService.putJSON(_makeApiUrl(path), - appendAuthorizationToken: true); + Future enableNewPostNotificationsForUserWithUsername(String userUsername) { + String path = _makeEnableNewPostNotificationsForUserWithUsernamePath(userUsername); + return _httpService.putJSON(_makeApiUrl(path), appendAuthorizationToken: true); } - Future disableNewPostNotificationsForUserWithUsername( - String userUsername) { - String path = - _makeEnableNewPostNotificationsForUserWithUsernamePath(userUsername); - return _httpService.delete(_makeApiUrl(path), - appendAuthorizationToken: true); + Future disableNewPostNotificationsForUserWithUsername(String userUsername) { + String path = _makeEnableNewPostNotificationsForUserWithUsernamePath(userUsername); + return _httpService.delete(_makeApiUrl(path), appendAuthorizationToken: true); } Future searchFollowers({@required String query, int count}) { @@ -358,7 +353,12 @@ class AuthApiService { } Future updateAuthenticatedUserNotificationsSettings({ - bool postNotifications, + bool postCommentNotifications, + bool postCommentReplyNotifications, + bool postCommentReactionNotifications, + bool postCommentUserMentionNotifications, + bool postUserMentionNotifications, + bool postReactionNotifications, bool followNotifications, bool connectionRequestNotifications, bool connectionConfirmedNotifications, @@ -368,8 +368,25 @@ class AuthApiService { }) { Map body = {}; - if (postNotifications != null) - body['post_notifications'] = postNotifications; + if (postCommentNotifications != null) + body['post_comment_notifications'] = postCommentNotifications; + + if (postCommentReplyNotifications != null) + body['post_comment_reply_notifications'] = postCommentReplyNotifications; + + if (postCommentUserMentionNotifications != null) + body['post_comment_user_mention_notifications'] = + postCommentUserMentionNotifications; + + if (postUserMentionNotifications != null) + body['post_user_mention_notifications'] = postUserMentionNotifications; + + if (postCommentReactionNotifications != null) + body['post_comment_reaction_notifications'] = + postCommentReactionNotifications; + + if (postReactionNotifications != null) + body['post_reaction_notifications'] = postReactionNotifications; if (followNotifications != null) body['follow_notifications'] = followNotifications; @@ -439,11 +456,9 @@ class AuthApiService { .parse(UNBLOCK_USER_PATH, {'userUsername': username}); } - String _makeEnableNewPostNotificationsForUserWithUsernamePath( - String username) { - return _stringTemplateService.parse( - ENABLE_NEW_POST_NOTIFICATIONS_FOR_USER_PATH, - {'userUsername': username}); + String _makeEnableNewPostNotificationsForUserWithUsernamePath(String username) { + return _stringTemplateService + .parse(ENABLE_NEW_POST_NOTIFICATIONS_FOR_USER_PATH, {'userUsername': username}); } String _makeReportUserPath({@required username}) { diff --git a/lib/services/bottom_sheet.dart b/lib/services/bottom_sheet.dart index 84ac3e0df..927feea1f 100644 --- a/lib/services/bottom_sheet.dart +++ b/lib/services/bottom_sheet.dart @@ -17,8 +17,6 @@ import 'package:Okuna/pages/home/bottom_sheets/hashtag_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/hashtags_display_setting_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/image_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/link_previews_setting_picker.dart'; -import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_comment_notifications.dart'; -import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart'; import 'package:Okuna/pages/home/bottom_sheets/post_comment_more_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/follows_lists_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/post_actions.dart'; @@ -176,48 +174,13 @@ class BottomSheetService { displayContext: displayContext, onCommunityExcluded: onCommunityExcluded, onUndoCommunityExcluded: onUndoCommunityExcluded, - onPostCommunityExcludedFromProfilePosts: - onPostCommunityExcludedFromProfilePosts, + onPostCommunityExcludedFromProfilePosts: onPostCommunityExcludedFromProfilePosts, onPostDeleted: onPostDeleted, onPostReported: onPostReported, ); }); } - Future showManagePostNotifications( - {@required BuildContext context, - @required Post post, - VoidCallback onNotificationSettingsSave}) { - return _showModalBottomSheetApp( - context: context, - builder: (BuildContext context) { - return Material( - child: OBManagePostNotificationsBottomSheet( - post: post, - onNotificationSettingsSave: onNotificationSettingsSave, - ), - ); - }); - } - - Future showManagePostCommentNotifications( - {@required BuildContext context, - @required Post post, - @required PostComment postComment, - VoidCallback onNotificationSettingsSave}) { - return _showModalBottomSheetApp( - context: context, - builder: (BuildContext context) { - return Material( - child: OBManagePostCommentNotificationsBottomSheet( - post: post, - postComment: postComment, - onNotificationSettingsSave: onNotificationSettingsSave, - ), - ); - }); - } - Future showHashtagActions( {@required BuildContext context, @required Hashtag hashtag, diff --git a/lib/services/localization.dart b/lib/services/localization.dart index f3bb7f79d..bf6dc7298 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -2188,114 +2188,6 @@ class LocalizationService { name: 'post__exclude_community_from_profile_posts_confirmation'); } - String get post__manage_post_notifications { - return Intl.message("Manage post notifications", - name: 'post__manage_post_notifications'); - } - - String get post__manage_post_comment_notifications { - return Intl.message("Manage comment notifications", - name: 'post__manage_post_comment_notifications'); - } - - String get post__subscribe_post_notifications { - return Intl.message("Subscribe to post notifications", - name: 'post__subscribe_post_notifications'); - } - - String get post__subscribe_post_comment_notifications { - return Intl.message("Subscribe to comment notifications", - name: 'post__subscribe_post_comment_notifications'); - } - - String get post__mute_post_notifications_text { - return Intl.message("Mute post", - name: 'post__mute_post_notifications_text'); - } - - String get post__unmute_post_notifications_text { - return Intl.message("Unmute post", - name: 'post__unmute_post_notifications_text'); - } - - String get post__mute_post_comment_notifications_text { - return Intl.message("Mute comment", - name: 'post__mute_post_comment_notifications_text'); - } - - String get post__unmute_post_comment_notifications_text { - return Intl.message("Unmute comment", - name: 'post__unmute_post_comment_notifications_text'); - } - - String get post__manage_notifications_comments_title { - return Intl.message("Comments", - name: 'post__manage_notifications_comments_title'); - } - - String get post__manage_notifications_comments_desc { - return Intl.message("Get notifications when someone comments on this post", - name: 'post__manage_notifications_comments_desc'); - } - - String get post__manage_notifications_comment_reactions_title { - return Intl.message("Comment Reactions", - name: 'post__manage_notifications_comment_reactions_title'); - } - - String get post__manage_notifications_comment_reactions_desc { - return Intl.message( - "Get notifications when someone reacts to your comments", - name: 'post__manage_notifications_comment_reactions_desc'); - } - - String get post__manage_notifications_reactions_title { - return Intl.message("Reactions", - name: 'post__manage_notifications_reactions_title'); - } - - String get post__manage_notifications_reactions_desc { - return Intl.message("Get notifications when someone reacts to your post", - name: 'post__manage_notifications_reactions_desc'); - } - - String get post__manage_notifications_reactions_post_comment_desc { - return Intl.message("Get notifications when someone reacts to your comment", - name: 'post__manage_notifications_reactions_post_comment_desc'); - } - - String get post__manage_notifications_replies_title { - return Intl.message("Replies", - name: 'post__manage_notifications_replies_title'); - } - - String get post__manage_notifications_replies_desc { - return Intl.message( - "Get notifications when someone replies to comments on this post", - name: 'post__manage_notifications_replies_desc'); - } - - String get post__manage_notifications_replies_post_comment_desc { - return Intl.message( - "Get notifications when someone replies to this comment", - name: 'post__manage_notifications_replies_post_comment_desc'); - } - - String get post__manage_notifications_successfully_saved { - return Intl.message("Notification settings saved", - name: 'post__manage_notifications_successfully_saved'); - } - - String get post__manage_notifications_successfully_muted { - return Intl.message("Notifications muted", - name: 'post__manage_notifications_successfully_muted'); - } - - String get post__manage_notifications_successfully_unmuted { - return Intl.message("Notifications unmuted", - name: 'post__manage_notifications_successfully_unmuted'); - } - String get post__comments_enabled_message { return Intl.message("Comments enabled for post", name: 'post__comments_enabled_message'); @@ -3464,15 +3356,68 @@ class LocalizationService { name: 'notifications__connection_desc'); } - String get notifications__post_notifications_title { - return Intl.message("Post notifications", - name: 'notifications__post_notifications_title'); + String get notifications__comment_title { + return Intl.message("Post comment", name: 'notifications__comment_title'); + } + + String get notifications__comment_desc { + return Intl.message( + "Be notified when someone comments on one of your posts or one you also commented", + name: 'notifications__comment_desc'); + } + + String get notifications__comment_reply_title { + return Intl.message("Post comment reply", + name: 'notifications__comment_reply_title'); + } + + String get notifications__comment_reply_desc { + return Intl.message( + "Be notified when someone replies to one of your comments or one you also replied to", + name: 'notifications__comment_reply_desc'); + } + + String get notifications__comment_user_mention_title { + return Intl.message("Post comment mention", + name: 'notifications__comment_user_mention_title'); } - String get notifications__post_notifications_desc { + String get notifications__comment_user_mention_desc { return Intl.message( - "Be notified of all post related notifications - comments, replies, reactions and user mentions", - name: 'notifications__post_notifications_desc'); + "Be notified when someone mentions you on one of their comments", + name: 'notifications__comment_user_mention_desc'); + } + + String get notifications__post_user_mention_title { + return Intl.message("Post mention", + name: 'notifications__post_user_mention_title'); + } + + String get notifications__post_user_mention_desc { + return Intl.message( + "Be notified when someone mentions you on one of their posts", + name: 'notifications__post_user_mention_desc'); + } + + String get notifications__comment_reaction_title { + return Intl.message("Post comment reaction", + name: 'notifications__comment_reaction_title'); + } + + String get notifications__comment_reaction_desc { + return Intl.message( + "Be notified when someone reacts to one of your post commments", + name: 'notifications__comment_reaction_desc'); + } + + String get notifications__post_reaction_title { + return Intl.message("Post reaction", + name: 'notifications__post_reaction_title'); + } + + String get notifications__post_reaction_desc { + return Intl.message("Be notified when someone reacts to one of your posts", + name: 'notifications__post_reaction_desc'); } String get notifications__community_invite_title { @@ -3518,16 +3463,6 @@ class LocalizationService { name: 'notifications__mute_post_turn_off_post_notifications'); } - String get notifications__enable_comment_post_notifications { - return Intl.message("Enable new comment notifications", - name: 'notifications__enable_comment_post_notifications'); - } - - String get notifications__disable_comment_post_notifications { - return Intl.message("Disable new comment notifications", - name: 'notifications__disable_comment_post_notifications'); - } - String get notifications__mute_post_turn_on_post_comment_notifications { return Intl.message("Turn on post comment notifications", name: 'notifications__mute_post_turn_on_post_comment_notifications'); diff --git a/lib/services/modal_service.dart b/lib/services/modal_service.dart index 7d2ca747f..535dd3fbc 100644 --- a/lib/services/modal_service.dart +++ b/lib/services/modal_service.dart @@ -106,7 +106,6 @@ class ModalService { Future openExpandedReplyCommenter( {@required BuildContext context, @required PostComment postComment, - PostComment postCommentReply, @required Post post, @required Function(PostComment) onReplyAdded, @required Function(PostComment) onReplyDeleted}) async { @@ -118,7 +117,6 @@ class ModalService { child: OBPostCommentReplyExpandedModal( post: post, postComment: postComment, - postCommentReply: postCommentReply, onReplyAdded: onReplyAdded, onReplyDeleted: onReplyDeleted), ); diff --git a/lib/services/posts_api.dart b/lib/services/posts_api.dart index 01bee00c5..fd7853fa4 100644 --- a/lib/services/posts_api.dart +++ b/lib/services/posts_api.dart @@ -42,10 +42,6 @@ class PostsApiService { 'api/posts/{postUuid}/comments/{postCommentId}/replies/'; static const MUTE_POST_PATH = 'api/posts/{postUuid}/notifications/mute/'; static const UNMUTE_POST_PATH = 'api/posts/{postUuid}/notifications/unmute/'; - static const POST_NOTIFICATIONS_SUBSCRIPTION_PATH = - 'api/posts/{postUuid}/notifications/subscribe/'; - static const POST_COMMENT_NOTIFICATIONS_SUBSCRIPTION_PATH = - 'api/posts/{postUuid}/comments/{postCommentId}/notifications/subscribe/'; static const REPORT_POST_PATH = 'api/posts/{postUuid}/report/'; static const PREVIEW_POST_DATA_PATH = 'api/posts/{postUuid}/link-preview/'; static const TRANSLATE_POST_PATH = 'api/posts/{postUuid}/translate/'; @@ -441,73 +437,6 @@ class PostsApiService { return _httpService.post(_makeApiUrl(path), appendAuthorizationToken: true); } - Future createPostNotificationsSubscription( - {@required String postUuid, - bool commentNotifications = false, - bool reactionNotifications = false, - bool replyNotifications = false}) { - Map body = {}; - - body['comment_notifications'] = commentNotifications ?? false; - body['reaction_notifications'] = reactionNotifications ?? false; - body['reply_notifications'] = replyNotifications ?? false; - String path = _makePostNotificationsSubscriptionPath(postUuid); - return _httpService.putJSON(_makeApiUrl(path), - body: body, appendAuthorizationToken: true); - } - - Future updatePostNotificationsSubscription( - {@required String postUuid, - bool commentNotifications, - bool reactionNotifications, - bool replyNotifications}) { - Map body = {}; - if (commentNotifications != null) { - body['comment_notifications'] = commentNotifications; - } - if (reactionNotifications != null) { - body['reaction_notifications'] = reactionNotifications; - } - if (replyNotifications != null) { - body['reply_notifications'] = replyNotifications; - } - String path = _makePostNotificationsSubscriptionPath(postUuid); - return _httpService.patchJSON(_makeApiUrl(path), - body: body, appendAuthorizationToken: true); - } - - Future createPostCommentNotificationsSubscription( - {@required String postUuid, - @required int postCommentId, - bool reactionNotifications = false, - bool replyNotifications = false}) { - Map body = {}; - body['reaction_notifications'] = reactionNotifications ?? false; - body['reply_notifications'] = replyNotifications ?? false; - String path = _makePostCommentNotificationsSubscriptionPath( - postUuid: postUuid, postCommentId: postCommentId); - return _httpService.putJSON(_makeApiUrl(path), - body: body, appendAuthorizationToken: true); - } - - Future updatePostCommentNotificationsSubscription( - {@required String postUuid, - @required int postCommentId, - bool reactionNotifications, - bool replyNotifications}) { - Map body = {}; - if (reactionNotifications != null) { - body['reaction_notifications'] = reactionNotifications; - } - if (replyNotifications != null) { - body['reply_notifications'] = replyNotifications; - } - String path = _makePostCommentNotificationsSubscriptionPath( - postUuid: postUuid, postCommentId: postCommentId); - return _httpService.patchJSON(_makeApiUrl(path), - body: body, appendAuthorizationToken: true); - } - Future disableCommentsForPostWithUuidPost(String postUuid) { String path = _makeDisableCommentsForPostPath(postUuid); return _httpService.post(_makeApiUrl(path), appendAuthorizationToken: true); @@ -701,20 +630,6 @@ class PostsApiService { .parse(UNMUTE_POST_PATH, {'postUuid': postUuid}); } - String _makePostNotificationsSubscriptionPath(String postUuid) { - return _stringTemplateService - .parse(POST_NOTIFICATIONS_SUBSCRIPTION_PATH, {'postUuid': postUuid}); - } - - String _makePostCommentNotificationsSubscriptionPath({ - @required int postCommentId, - @required String postUuid, - }) { - return _stringTemplateService.parse( - POST_COMMENT_NOTIFICATIONS_SUBSCRIPTION_PATH, - {'postUuid': postUuid, 'postCommentId': postCommentId}); - } - String _makeMutePostCommentPath({ @required int postCommentId, @required String postUuid, diff --git a/lib/services/user.dart b/lib/services/user.dart index 92f424910..f470d4bc7 100644 --- a/lib/services/user.dart +++ b/lib/services/user.dart @@ -36,7 +36,6 @@ import 'package:Okuna/models/post_comment_list.dart'; import 'package:Okuna/models/post_comment_reaction.dart'; import 'package:Okuna/models/post_comment_reaction_list.dart'; import 'package:Okuna/models/post_media_list.dart'; -import 'package:Okuna/models/post_notifications_subscription.dart'; import 'package:Okuna/models/post_reaction.dart'; import 'package:Okuna/models/post_reaction_list.dart'; import 'package:Okuna/models/reactions_emoji_count_list.dart'; @@ -766,9 +765,6 @@ class UserService { HttpieResponse response = await _postsApiService.commentPost(postUuid: post.uuid, text: text); _checkResponseIsCreated(response); - // refresh post to get notifications subscription model - if (post.postNotificationsSubscription == null) - await getPostWithUuid(post.uuid); return PostComment.fromJSON(json.decode(response.body)); } @@ -797,9 +793,6 @@ class UserService { HttpieResponse response = await _postsApiService.replyPostComment( postUuid: post.uuid, postCommentId: postComment.id, text: text); _checkResponseIsCreated(response); - // refresh post to get notifications subscription model - if (post.postNotificationsSubscription == null) - await getPostWithUuid(post.uuid); return PostComment.fromJSON(json.decode(response.body)); } @@ -824,66 +817,6 @@ class UserService { return Post.fromJson(json.decode(response.body)); } - Future createPostNotificationsSubscription( - {@required Post post, - bool commentNotifications, - bool reactionNotifications, - bool replyNotifications}) async { - HttpieResponse response = - await _postsApiService.createPostNotificationsSubscription( - postUuid: post.uuid, - commentNotifications: commentNotifications, - reactionNotifications: reactionNotifications, - replyNotifications: replyNotifications); - _checkResponseIsCreated(response); - return Post.fromJson(json.decode(response.body)); - } - - Future updatePostNotificationsSubscription( - {@required Post post, - bool commentNotifications, - bool reactionNotifications, - bool replyNotifications}) async { - HttpieResponse response = - await _postsApiService.updatePostNotificationsSubscription( - postUuid: post.uuid, - commentNotifications: commentNotifications, - reactionNotifications: reactionNotifications, - replyNotifications: replyNotifications); - _checkResponseIsOk(response); - return Post.fromJson(json.decode(response.body)); - } - - Future createPostCommentNotificationsSubscription( - {@required Post post, - @required PostComment postComment, - bool reactionNotifications, - bool replyNotifications}) async { - HttpieResponse response = - await _postsApiService.createPostCommentNotificationsSubscription( - postUuid: post.uuid, - postCommentId: postComment.id, - reactionNotifications: reactionNotifications, - replyNotifications: replyNotifications); - _checkResponseIsCreated(response); - return Post.fromJson(json.decode(response.body)); - } - - Future updatePostCommentNotificationsSubscription( - {@required Post post, - @required PostComment postComment, - bool reactionNotifications, - bool replyNotifications}) async { - HttpieResponse response = - await _postsApiService.updatePostCommentNotificationsSubscription( - postUuid: post.uuid, - postCommentId: postComment.id, - reactionNotifications: reactionNotifications, - replyNotifications: replyNotifications); - _checkResponseIsOk(response); - return Post.fromJson(json.decode(response.body)); - } - Future excludeCommunityFromTopPosts(Community community) async { HttpieResponse response = await _postsApiService .excludeCommunityFromTopPosts(communityName: community.name); @@ -2020,7 +1953,12 @@ class UserService { Future updateAuthenticatedUserNotificationsSettings({ - bool postNotifications, + bool postCommentNotifications, + bool postCommentReplyNotifications, + bool postCommentReactionNotifications, + bool postCommentUserMentionNotifications, + bool postUserMentionNotifications, + bool postReactionNotifications, bool followNotifications, bool connectionRequestNotifications, bool connectionConfirmedNotifications, @@ -2030,7 +1968,13 @@ class UserService { }) async { HttpieResponse response = await _authApiService.updateAuthenticatedUserNotificationsSettings( - postNotifications: postNotifications, + postCommentNotifications: postCommentNotifications, + postCommentReplyNotifications: postCommentReplyNotifications, + postCommentUserMentionNotifications: + postCommentUserMentionNotifications, + postUserMentionNotifications: postUserMentionNotifications, + postCommentReactionNotifications: postCommentReactionNotifications, + postReactionNotifications: postReactionNotifications, followNotifications: followNotifications, connectionConfirmedNotifications: connectionConfirmedNotifications, communityInviteNotifications: communityInviteNotifications, diff --git a/lib/widgets/fields/toggle_field.dart b/lib/widgets/fields/toggle_field.dart index 530b83734..ce898a447 100644 --- a/lib/widgets/fields/toggle_field.dart +++ b/lib/widgets/fields/toggle_field.dart @@ -13,7 +13,6 @@ class OBToggleField extends StatelessWidget { final bool hasDivider; final TextStyle titleStyle; final bool isLoading; - final bool isDisabled; const OBToggleField( {Key key, @@ -25,7 +24,6 @@ class OBToggleField extends StatelessWidget { this.subtitle, this.hasDivider = true, this.titleStyle, - this.isDisabled = false, this.isLoading = false}) : super(key: key); @@ -46,20 +44,16 @@ class OBToggleField extends StatelessWidget { subtitle: subtitle, trailing: CupertinoSwitch( value: value, - onChanged: (bool newValue) { - if (!isDisabled) onChanged(newValue); - }, + onChanged: onChanged, ), - onTap: () { - if (!isDisabled) onTap(); - }), + onTap: onTap), ), hasDivider ? OBDivider() : const SizedBox() ], ), ); - if (isLoading || isDisabled) { + if (isLoading) { tile = Opacity( opacity: 0.5, child: tile, diff --git a/lib/widgets/tiles/actions/manage_post_comment_notifications_tile.dart b/lib/widgets/tiles/actions/manage_post_comment_notifications_tile.dart deleted file mode 100644 index be4097905..000000000 --- a/lib/widgets/tiles/actions/manage_post_comment_notifications_tile.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:Okuna/models/post.dart'; -import 'package:Okuna/models/post_comment.dart'; -import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart'; -import 'package:Okuna/provider.dart'; -import 'package:Okuna/services/bottom_sheet.dart'; -import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/widgets/icon.dart'; -import 'package:Okuna/widgets/theming/text.dart'; -import 'package:flutter/material.dart'; - -class OBManagePostCommentNotificationsTile extends StatefulWidget { - final Post post; - final PostComment postComment; - final OnNotificationSettingsSave onNotificationSettingsSave; - - const OBManagePostCommentNotificationsTile({ - Key key, - @required this.post, - @required this.postComment, - this.onNotificationSettingsSave, - }) : super(key: key); - - @override - OBManagePostCommentNotificationsTileState createState() { - return OBManagePostCommentNotificationsTileState(); - } -} - -class OBManagePostCommentNotificationsTileState - extends State { - BottomSheetService _bottomSheetService; - LocalizationService _localizationService; - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - var openbookProvider = OpenbookProvider.of(context); - _bottomSheetService = openbookProvider.bottomSheetService; - _localizationService = openbookProvider.localizationService; - - return StreamBuilder( - stream: widget.postComment.updateSubject, - initialData: widget.postComment, - builder: (BuildContext context, AsyncSnapshot snapshot) { - var postComment = snapshot.data; - - return ListTile( - leading: OBIcon(OBIcons.notifications), - title: OBText(postComment.postCommentNotificationsSubscription != null - ? _localizationService.post__manage_post_comment_notifications - : _localizationService - .post__subscribe_post_comment_notifications), - onTap: _navigateToManagePostComment, - ); - }, - ); - } - - void _navigateToManagePostComment() { - _bottomSheetService.showManagePostCommentNotifications( - context: context, - post: widget.post, - postComment: widget.postComment, - onNotificationSettingsSave: () { - if (widget.onNotificationSettingsSave != null) - widget.onNotificationSettingsSave(); - }); - } -} diff --git a/lib/widgets/tiles/actions/manage_post_notifications_tile.dart b/lib/widgets/tiles/actions/manage_post_notifications_tile.dart deleted file mode 100644 index 72e24d116..000000000 --- a/lib/widgets/tiles/actions/manage_post_notifications_tile.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:Okuna/models/post.dart'; -import 'package:Okuna/pages/home/bottom_sheets/manage_notifications/manage_post_notifications.dart'; -import 'package:Okuna/provider.dart'; -import 'package:Okuna/services/bottom_sheet.dart'; -import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/widgets/icon.dart'; -import 'package:Okuna/widgets/theming/text.dart'; -import 'package:flutter/material.dart'; - -class OBManagePostNotificationsTile extends StatefulWidget { - final Post post; - final OnNotificationSettingsSave onNotificationSettingsSave; - - const OBManagePostNotificationsTile({ - Key key, - @required this.post, - this.onNotificationSettingsSave, - }) : super(key: key); - - @override - OBManagePostNotificationsTileState createState() { - return OBManagePostNotificationsTileState(); - } -} - -class OBManagePostNotificationsTileState - extends State { - BottomSheetService _bottomSheetService; - LocalizationService _localizationService; - bool _requestInProgress; - - @override - void initState() { - super.initState(); - _requestInProgress = false; - } - - @override - Widget build(BuildContext context) { - var openbookProvider = OpenbookProvider.of(context); - _bottomSheetService = openbookProvider.bottomSheetService; - _localizationService = openbookProvider.localizationService; - - return StreamBuilder( - stream: widget.post.updateSubject, - initialData: widget.post, - builder: (BuildContext context, AsyncSnapshot snapshot) { - var post = snapshot.data; - - return ListTile( - leading: OBIcon(OBIcons.notifications), - title: OBText(post.postNotificationsSubscription != null - ? _localizationService.post__manage_post_notifications - : _localizationService.post__subscribe_post_notifications), - onTap: _navigateToManagePost, - ); - }, - ); - } - - void _navigateToManagePost() { - _bottomSheetService.showManagePostNotifications( - context: context, - post: widget.post, - onNotificationSettingsSave: () { - if (widget.onNotificationSettingsSave != null) - widget.onNotificationSettingsSave(); - }); - } -} From b537d34ecd9a1966fa793a4b2e016057bd5e7a40 Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Wed, 6 May 2020 13:57:30 +0200 Subject: [PATCH 34/39] Revert "Merge pull request #514 from OkunaOrg/feature/257-versioning-trending-communities-n-posts" This reverts commit 982804da7ab3c8a56e952ddd4b2f4691c353b2fe, reversing changes made to 73c2b0a52dd4364948f33f7547d84e94e7a1b6f8. --- lib/services/communities_api.dart | 3 +-- lib/services/posts_api.dart | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/services/communities_api.dart b/lib/services/communities_api.dart index 8e441a12c..36e44cfac 100644 --- a/lib/services/communities_api.dart +++ b/lib/services/communities_api.dart @@ -116,8 +116,7 @@ class CommunitiesApiService { return _httpService.get('$apiURL$GET_TRENDING_COMMUNITIES_PATH', queryParameters: queryParams, - appendAuthorizationToken: authenticatedRequest, - headers: {'Accept': 'application/json; version=2.0'}); + appendAuthorizationToken: authenticatedRequest); } Future getSuggestedCommunities( diff --git a/lib/services/posts_api.dart b/lib/services/posts_api.dart index fd7853fa4..1e5053771 100644 --- a/lib/services/posts_api.dart +++ b/lib/services/posts_api.dart @@ -24,7 +24,7 @@ class PostsApiService { 'api/posts/profile/excluded-communities/{communityName}/'; static const EXCLUDED_PROFILE_POSTS_COMMUNITIES_SEARCH_PATH = 'api/posts/profile/excluded-communities/search/'; - static const GET_TRENDING_POSTS_PATH = 'api/posts/trending/'; + static const GET_TRENDING_POSTS_PATH = 'api/posts/trending/new/'; static const CREATE_POST_PATH = 'api/posts/'; static const POST_MEDIA_PATH = 'api/posts/{postUuid}/media/'; static const EDIT_POST_PATH = 'api/posts/{postUuid}/'; @@ -126,8 +126,7 @@ class PostsApiService { return _httpService.get('$apiURL$GET_TRENDING_POSTS_PATH', queryParameters: queryParams, - appendAuthorizationToken: authenticatedRequest, - headers: {'Accept': 'application/json; version=2.0'}); + appendAuthorizationToken: authenticatedRequest); } Future getTimelinePosts( From 5a274cc4668c4d28b0987b8ba096e50a51960e46 Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Wed, 6 May 2020 18:34:28 +0200 Subject: [PATCH 35/39] :recycle: rename methods, add localization, change icon image --- assets/images/icons/camera-icon.png | Bin 0 -> 5180 bytes .../home/modals/save_post/create_post.dart | 2 +- lib/services/localization.dart | 5 +++ lib/services/media/media.dart | 23 +++++------ lib/services/share.dart | 37 ++++++++---------- lib/widgets/icon.dart | 1 + pubspec.yaml | 1 + 7 files changed, 33 insertions(+), 36 deletions(-) create mode 100644 assets/images/icons/camera-icon.png diff --git a/assets/images/icons/camera-icon.png b/assets/images/icons/camera-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..286b0d886c3634424ef09ce2fe42484ee8b84e65 GIT binary patch literal 5180 zcmaJ_Ra6uJ(_H}(kQ50C>1JW+R6 zg+#+6KoRhcQLugIPu7P~!IA>h7iCSomH)D^0P4|UxPER@LhAqYf>G)Q1Fmu!xtlvbfT7^(!@1QT zuEo=fXky4gUU|BCPfWi~*%^$6d}PuHYL4ZyMSSvU04!>Jx)*DcOK|?LK!^9NMc&*x z6KRK42;Rws$~hJW5K5K#Pu28Rs+RkGSZ}QMT~_#-$KyeT3`IKBBa9re4gLLYiy&Ie zT(tiNXeNw!i>QKDk>ZgS<{O>xA=JSn1%U*EH(YE1pTU~k1Kt=XbdNo9&?T~bw-`>+ z-=q_$ZQ=KwCE$JDYZ7QZ)O!&ijQu!RVsWnsGZ*d3xT6>QIt`ztamuYpKb>}$4byQu zf8CuSqF}XM|KgjXI9Dont{B>nTg!JR#;p!}nP(^}_m)UL`jA#2z4Fae0O*plmTiootYj3r9)m$^! z(D2WDc0~*Dlp5@zeBJt!kwWpTvh8j#9-e&+GT+^M*;Nq<8qZXGaI_|ckCGu#j2^DZ zy^e$9SZMf0<5{?)Kieq1hDH?KB=M4^^28LUYJ6U}_sVFr7SiO@rTMJc2$g4r*f>wv zk!7YUOhlH7=E1tXH+z*9S?i6!4HG27t2RFsh2ymJGAoyk#w-Lve* z-MxMPe27Eq66(Fr-y@ODX04|S;d;YX@SRG-WLav1nu$(w8ik_|eJxP>gy(C!hCYAl zdr=SB{z8*c;i6NCguOZH8+T}<5{)3UDd~#W!#F=pQ(n^udk};DB?X1zpbcS(~)GEH`ZGE4?N8=z2VfDUAb#O}@`m9jQ z5`uVAI;@)5)ZAxr(g4R{3Zxq2r@*oma{V7eC7d^Wl1EB4Xi2khLD9xO|0bhsZ|6vi zFlj3!K#b!sLM2n~QiJW2w)Lc#(UF4DU$_``wq+?-$p>|*m9M|o7$Uqg>%a&HAC#Vw zzXw9WXlt#m;gSW2M{w~O)HANTZaC3d z@9xD+`~6MPS;R^}8skp<7&9nL|9d_tz`V?EpF;6d#Cm4P{2wAtO>!L*Ijsr9WqPzxr?IiyUn!byAti-TMwHt0=@fH>D6Bmd{-q8NG?ko?n-~br^Bli@7j-`3;s~BTnnSKXrG-(`_;++tfFRBRZCpt4t5w=PCa{U_= zSN%474G4pp5h74*YC^i|;G@)eD(`1QRhF2cH0B75IHm8^CXpxvK1!!{(v!7}>Kg~F zy4|xT*RUy<*bfd5mvi_h8H84xCur>Wa~7-whPB=rF4wq5y+E(Q-WB*DDU6YtI{1Y7_KezqFJG4>^ADi&LG!C?1aYPB7U0Jo zbrJ~#Ll!F8CQ3jz(V*AzY~GT@%@UtX3(EX-RQ#I6e9?N5nQjMKa9*_v3J1xGV>p@_ zAfX>hIF|6H`NUG8oER(74$pfaAr-G`1%D7@rusm8Lqr6&|g#sBE9g00-S9w2rNBu?&92q1#-^RLpny;2Ls< zpzbgxd;3KoL~u!5Zc<6Za$mvaj0=B5d$t1fNu50cPk z#v7a1a$D%?IwrfOAiis5gq2T?NPMCl1e(x#TF$|)N5E&<-f2GDxr}Qsty)xl6MTlQ z6WzPe&j!*IzD}6guuhq~sb)>{0HzdfPqLVpUUMz~*KT03tkSD!twc*Jf z4E*8+1v2DL8Xz01WA2Gv_x?!uU5fuYswud0Z`l@aaqj{n9QAp`O9EYz-a^wttBZ14=5P`SNfzs6#&8v)X$t5^`!DHWhmoKclY2%|#?7A1 zQ^}xj%O}J(p{P|z6v6<3?-c=4DkSP0g18c`MoPb2e1u(S*=KzCHK#oemCp|?CS*hP z!^=GjM&^J93n7wFAHznS3BBl=RJ*SFc|XjfoWl*;bWVY#4L%hPR!t#%YJm_@rrAWJ z70(ZZ%U=l+e9Ki}-e(#!bLcqs^M6W8`jTBawOP$;je?5OWA&Yn(>dVRJ2sa!bl#mg zI`zWNOF+De(Sx*RrmZsWIa4~Z4+;kyg9c0OgrmHI^h0mxmm1OO=!%l|KEX0|4_#G& zrZRgK7JORDi3_MQ9!32ODO_a*5C`^4$UF?jV?y3|s7bO3r{VcSKM>V3vrpbLdSDRS7mMVlc~v$&DuKj=wk2$>yV!}=zmsO>V)xM*jk zs#))%wdNH2k3OEpKh#g40$pqrqg7AwS@~=`@8qE5at2DH;9&?cTct|rczJo^)ksCn z&nx+L(gh%gdNOfAisF^Xi{ zgg=rSIsJo7A(^j8c2>>Aehip4Ge;+KB?l5!OlrT#S-A1s-cav;!6RD@8ZY=oNY=nR zev28evox<5PR2X+XDnoTyVQV2Kf}Oa+lLH$^IouBx>?B~D1YKvVYqXK%z{>Aww7|V zADWu>1e?R?Nukkw|JLT|ZTCEsQr1I<=LFZtps%qosKCEqG^_BpI;=GWX0E+>x&1h&5Do1YhviK--=I+lUwUS4iv8N z)rk=L1{>nK_ZadK>0io6gQF5vls^>%4&MwDRS+@G{L_8?=a3!q87H^uUc7i_Gz$p9Qm@ zODU@_nGN0(YDU++6H2!L)3t6>@-vgeO|I9~wyGLZL^deQvAFeGZ;wd7o=T>1XfZB^ zr2hOu#AK z9jD_DxV#u4r$Xem$6T74lxe+mQ#=+53Dvy$mcuAac!!PVuVQDTGu?fxC{r4+e=82C znzY|bcT-94^G}!>;)+ggm*n)$L~9MC(y#bM7ZZ&;tw!P6gl&(rYO$yHEf(2^nOU*; z(Em%Ks^%E1XfsrwBVCc0RN11iwM|?(kU^x9CgWIBWEdyGd0kxwXYmji1J#A9d+33$ z-g=s>4HeANqz_(lcrZ{DN7clS2a}i_70WmVQcRA+9o*2e0<&4dBRpiT}x+f2fYl$>uC#@y2qzybdYh zcqe8MAtCW;Ld(L`l;u=a+-p%VKbUc#GH_x=bEpfbsPE*RlT%l=6lpYa(U~^m= zJ7Xk^jBL?h)PT56ttmo`o^fM>mXsu#40w^RCM+5w=|S<0%X1}R45My4I-BY`2}PB8 zov%5twfHB2VU5-*_Zfwj^pRXMk}TooP{NUON;aS>fQ+r^(*bBfP}g>YA2-&io2oa4 zRKyUOpNy+QB{)-d1P{(ve^eVrzsDWRM0Q^KjX~vvS^SIboU$(-CcZ6V`3h5%4=YO_ zz6n+uZ=e$7xK+Hea;Fq>=3!JK`U2j?7a`00{<@8*y%B-Wt|``9dzZHv$b0X%^l0{$ zD0lLhULgbH=>^xXN%t@t4x{RMU7QIDg0Ipb<>7*B=pOu|q z$gM@O({8%{>wDiDVGNyJZ(&2WPw}E0Oh##ZpW1%JzsUTeul)Y8i+{$_hqz2~pL{l| z*e%2z(6CgHRC za^SdqgJw3$%A7q+zs9}jL@Y4eNy7;zhMWJ^DV<_>Yj^yNjiVtP!^@C5AU*eyT$}he zgEp;@|Ci96_OBHxJOOIU{}jHcZSG*h9=PjYG(Q?lp7_Ic0>WFprW$gNjqPgli`i{1 z1oeRAu}ak;n4T{;=|dLX9;pkQj!h;$;wRH}srE>7OWQVbu52CYwCOFFAbP^HY2Y_h zxgrL3E}iM>-?T_21x)i^6gjL;a1`^_&~BW!(Vi~#S zVYPe0tl5p90W{dSfi!&){hUpzuOvkEZ(47cv558Dti_g{H=^cS z?m8|tWm+jN=1g~IUkLJ|+h=nZD8fY>rn6;$5qeOgz;z>T4iUPpHl;?FUpjWO>mg`w zsJlOXbk=nt3P^?WmY^XMV9Z^>^J-X)&x)l}g?69M>LfdCh?Q&cOu!@ z>#ODpPL&Z(o}*w~p`dKq-ZCy>DW<{*A1l;7iv?2ESu9#D zAB_ADCx5lnRUh5p(bKmG z%&kqXll=e3r^>K4b^S-hxOVtMypL_** literal 0 HcmV?d00001 diff --git a/lib/pages/home/modals/save_post/create_post.dart b/lib/pages/home/modals/save_post/create_post.dart index 6ce725b79..c5b7ebf4b 100644 --- a/lib/pages/home/modals/save_post/create_post.dart +++ b/lib/pages/home/modals/save_post/create_post.dart @@ -428,7 +428,7 @@ class OBSavePostModalState extends OBContextualSearchBoxState { OBPillButton( text: _localizationService.post__create_camera, color: Pigment.fromString('#50b1f2'), - icon: const OBIcon(OBIcons.video), + icon: const OBIcon(OBIcons.cameraCartoon), onPressed: () async { _unfocusTextField(); try { diff --git a/lib/services/localization.dart b/lib/services/localization.dart index 008ee2479..b4c994375 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -819,6 +819,11 @@ class LocalizationService { return Intl.message("Unknown error", name: 'error__unknown_error'); } + String error__receive_share_text_too_long(int limit) { + return Intl.message("Text is too long (limit: $limit characters)", + args: [limit], name: 'image_picker__error_too_large'); + } + String get error__receive_share_temp_write_failed { return Intl.message('Failed to copy shared file to temporary location', name: 'error__receive_share_temp_write_failed'); diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart index e6b9f6e4e..77b2f4e81 100644 --- a/lib/services/media/media.dart +++ b/lib/services/media/media.dart @@ -99,7 +99,7 @@ class MediaService { return null; } - media = await _prepareMedia( + media = await processMedia( media: media, context: context, flattenGifs: flattenGifs, @@ -120,7 +120,7 @@ class MediaService { if (pickedImage == null) return null; - var media = await _prepareMedia( + var media = await processMedia( media: MediaFile(pickedImage, FileType.image), context: context, flattenGifs: true, @@ -140,7 +140,7 @@ class MediaService { if (pickedVideo == null) return null; - var media = await _prepareMedia( + var media = await processMedia( media: MediaFile(pickedVideo, FileType.video), context: context, ); @@ -148,7 +148,7 @@ class MediaService { return media.file; } - Future _prepareMedia( + Future processMedia( {@required MediaFile media, @required BuildContext context, bool flattenGifs = false, @@ -178,9 +178,9 @@ class MediaService { MediaFile copiedMedia = MediaFile(copiedFile, mediaType); if (mediaType == FileType.image) { - result = await _prepareImage(copiedMedia, tempPath, mediaUuid, imageType); + result = await _processImage(copiedMedia, tempPath, mediaUuid, imageType); } else if (mediaType == FileType.video) { - result = await _prepareVideo(copiedMedia); + result = await _processVideo(copiedMedia); } else { throw 'Unsupported media type: ${media.type}'; } @@ -188,7 +188,7 @@ class MediaService { return result; } - Future _prepareImage(MediaFile media, String tempPath, + Future _processImage(MediaFile media, String tempPath, String mediaUuid, OBImageType imageType) async { var image = await fixExifRotation(media.file, deleteOriginal: true); String processedImageName = mediaUuid + '.jpg'; @@ -205,9 +205,7 @@ class MediaService { throw FileTooLargeException( _validationService.getAllowedImageSize(imageType)); } - - processedImage = await processImage(processedImage); - + MediaFile result; if (imageType == OBImageType.post) { result = MediaFile(processedImage, media.type); @@ -224,7 +222,7 @@ class MediaService { return result; } - Future _prepareVideo(MediaFile media) async { + Future _processVideo(MediaFile media) async { if (!await _validationService.isVideoAllowedSize(media.file)) { throw FileTooLargeException(_validationService.getAllowedVideoSize()); } @@ -232,9 +230,6 @@ class MediaService { return media; } - Future processImage(File image) async { - return image; - } Future fixExifRotation(File image, {deleteOriginal: false}) async { List imageBytes = await image.readAsBytes(); diff --git a/lib/services/share.dart b/lib/services/share.dart index a77f2c850..fcd1138ea 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -4,9 +4,11 @@ import 'dart:io'; import 'package:Okuna/plugins/share/share.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/media/media.dart'; +import 'package:Okuna/services/media/models/media_file.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/validation.dart'; import 'package:async/async.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -126,31 +128,31 @@ class ShareService { if (share.image != null) { image = File.fromUri(Uri.parse(share.image)); - image = await _mediaService.fixExifRotation(image); - image = await _mediaService.processImage(image); - if (!await _validationService.isImageAllowedSize( - image, OBImageType.post)) { - _showFileTooLargeToast( - _validationService.getAllowedImageSize(OBImageType.post)); - return; - } + var processedFile = await _mediaService.processMedia( + media: MediaFile(image, FileType.image), + context: _context, + ); + image = processedFile.file; } if (share.video != null) { video = File.fromUri(Uri.parse(share.video)); - if (!await _validationService.isVideoAllowedSize(video)) { - _showFileTooLargeToast(_validationService.getAllowedVideoSize()); - return; - } + var processedFile = await _mediaService.processMedia( + media: MediaFile(image, FileType.video), + context: _context, + ); + + video = processedFile.file; } if (share.text != null) { text = share.text; if (!_validationService.isPostTextAllowedLength(text)) { + String errorMessage = _localizationService + .error__receive_share_text_too_long(ValidationService.POST_MAX_LENGTH); _toastService.error( - message: - 'Text too long (limit: ${ValidationService.POST_MAX_LENGTH} characters)', + message: errorMessage, context: _context); return; } @@ -169,11 +171,4 @@ class ShareService { } } } - - Future _showFileTooLargeToast(int limitInBytes) async { - _toastService.error( - message: _localizationService - .image_picker__error_too_large(limitInBytes ~/ 1048576), - context: _context); - } } diff --git a/lib/widgets/icon.dart b/lib/widgets/icon.dart index 137278bfe..70c4dc169 100644 --- a/lib/widgets/icon.dart +++ b/lib/widgets/icon.dart @@ -272,6 +272,7 @@ class OBIcons { static const profile = OBIconData(filename: 'profile-icon.png'); static const photo = OBIconData(filename: 'photo-icon.png'); static const video = OBIconData(filename: 'video-icon.png'); + static const cameraCartoon = OBIconData(filename: 'camera-icon.png'); static const gif = OBIconData(filename: 'gif-icon.png'); static const audience = OBIconData(filename: 'audience-icon.png'); static const burner = OBIconData(filename: 'burner-icon.png'); diff --git a/pubspec.yaml b/pubspec.yaml index 391356dd0..08e5519f3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -143,6 +143,7 @@ flutter: - assets/images/icons/chat-icon.png - assets/images/icons/photo-icon.png - assets/images/icons/video-icon.png + - assets/images/icons/camera-icon.png - assets/images/icons/warning-icon.png - assets/images/icons/success-icon.png - assets/images/icons/error-icon.png From df7afa5c2dc1837431ba58dbdf3b1229bf5ec58d Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Wed, 6 May 2020 19:02:15 +0200 Subject: [PATCH 36/39] :bookmark: up versions --- ios/Runner.xcodeproj/project.pbxproj | 56 +++++++++---------- .../home/pages/menu/pages/settings/about.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e924c6bd3..90a9ebcfa 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -1203,7 +1203,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1217,7 +1217,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1292,7 +1292,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1306,7 +1306,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1376,7 +1376,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1390,7 +1390,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1465,7 +1465,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1479,7 +1479,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1549,7 +1549,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1563,7 +1563,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1586,7 +1586,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1597,7 +1597,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; @@ -1623,7 +1623,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1634,7 +1634,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; @@ -1660,7 +1660,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1671,7 +1671,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; @@ -1697,7 +1697,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1708,7 +1708,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1732,7 +1732,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1743,7 +1743,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1767,7 +1767,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1778,7 +1778,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1802,7 +1802,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1813,7 +1813,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1938,7 +1938,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1952,7 +1952,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1971,7 +1971,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1985,7 +1985,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; diff --git a/lib/pages/home/pages/menu/pages/settings/about.dart b/lib/pages/home/pages/menu/pages/settings/about.dart index 26b70daf9..a12d669d6 100644 --- a/lib/pages/home/pages/menu/pages/settings/about.dart +++ b/lib/pages/home/pages/menu/pages/settings/about.dart @@ -48,7 +48,7 @@ class OBAboutPageState extends State { ListTile( leading: OBIcon(OBIcons.nativeInfo), title: OBText( - 'Okuna v0.0.65' + 'Okuna v0.0.66' ), ), ], diff --git a/pubspec.yaml b/pubspec.yaml index 08e5519f3..e4af09c5a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ description: Social Network # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # Read more about versioning at semver.org. -version: 0.0.65+82 +version: 0.0.66+83 environment: sdk: ">=2.1.0 <3.0.0" From 94336da9dded048b5aba82689b57dfcb075f626f Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Wed, 6 May 2020 19:04:19 +0200 Subject: [PATCH 37/39] :globe_with_meridians: add new locale strings --- assets/i18n/en/error.arb | 7 +++++++ lib/services/localization.dart | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/assets/i18n/en/error.arb b/assets/i18n/en/error.arb index 742dd9ddf..b918664ca 100644 --- a/assets/i18n/en/error.arb +++ b/assets/i18n/en/error.arb @@ -4,6 +4,13 @@ "type": "text", "placeholders": {} }, + "receive_share_text_too_long": "Text is too long (limit: {limit} characters)", + "@receive_share_text_too_long": { + "type": "text", + "placeholders": { + "limit": {} + } + }, "receive_share_temp_write_failed": "Failed to copy shared file to temporary location", "@receive_share_temp_write_failed": { "type": "text", diff --git a/lib/services/localization.dart b/lib/services/localization.dart index b4c994375..efabdfe6c 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -821,7 +821,7 @@ class LocalizationService { String error__receive_share_text_too_long(int limit) { return Intl.message("Text is too long (limit: $limit characters)", - args: [limit], name: 'image_picker__error_too_large'); + args: [limit], name: 'error__receive_share_text_too_long'); } String get error__receive_share_temp_write_failed { From b9b49deec2b4f5c5ed0e63e132489d43fc3fb46f Mon Sep 17 00:00:00 2001 From: Joel Hernandez Date: Fri, 8 May 2020 12:16:12 +0200 Subject: [PATCH 38/39] :globe_with_meridians: add extra i18n strings --- ios/Runner.xcodeproj/project.pbxproj | 13 ------------- lib/locale/messages_en.dart | 10 ++++++++-- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 90a9ebcfa..3e623a228 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 89398C16245F1D5F00F7C8DE /* Pods_Runner.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 898053AA22047AAF00E47AD9 /* Pods_Runner.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 89ABAE522203425900049DFB /* NotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 89ABAE512203425900049DFB /* NotificationService.m */; }; 89ABAE562203425900049DFB /* OneSignalNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 89ABAE4E2203425900049DFB /* OneSignalNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; @@ -306,17 +305,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 89398C17245F1D6000F7C8DE /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 89398C16245F1D5F00F7C8DE /* Pods_Runner.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; 89ABAE5F2203425900049DFB /* Embed App Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -589,7 +577,6 @@ 89ABAE4A2203425900049DFB /* Sources */, 89ABAE4B2203425900049DFB /* Frameworks */, 89ABAE4C2203425900049DFB /* Resources */, - 89398C17245F1D6000F7C8DE /* Embed Frameworks */, ); buildRules = ( ); diff --git a/lib/locale/messages_en.dart b/lib/locale/messages_en.dart index f4f07933e..c69a33bbf 100644 --- a/lib/locale/messages_en.dart +++ b/lib/locale/messages_en.dart @@ -61,6 +61,8 @@ class MessageLookup extends MessageLookupByLibrary { static m20(currentUserLanguage) => "Language (${currentUserLanguage})"; + static m67(limit) => "Text is too long (limit: ${limit} characters)"; + static m21(limit) => "File too large (limit: ${limit} MB)"; static m22(resourceCount, resourceName) => "See all ${resourceCount} ${resourceName}"; @@ -93,7 +95,7 @@ class MessageLookup extends MessageLookupByLibrary { static m36(description) => "Failed to preview link with website error: ${description}"; - static m67(link) => "Invalid link: ${link}"; + static m68(link) => "Invalid link: ${link}"; static m37(maxLength) => "Circle name must be no longer than ${maxLength} characters."; @@ -500,6 +502,7 @@ class MessageLookup extends MessageLookupByLibrary { "error__receive_share_invalid_uri_scheme" : MessageLookupByLibrary.simpleMessage("Failed to receive share"), "error__receive_share_temp_write_denied" : MessageLookupByLibrary.simpleMessage("Denied permission to copy shared file to temporary location"), "error__receive_share_temp_write_failed" : MessageLookupByLibrary.simpleMessage("Failed to copy shared file to temporary location"), + "error__receive_share_text_too_long" : m67, "error__unknown_error" : MessageLookupByLibrary.simpleMessage("Unknown error"), "image_picker__error_too_large" : m21, "image_picker__from_camera" : MessageLookupByLibrary.simpleMessage("From camera"), @@ -677,7 +680,9 @@ class MessageLookup extends MessageLookupByLibrary { "post__comments_page_tap_to_retry_replies" : MessageLookupByLibrary.simpleMessage("Tap to retry loading replies."), "post__comments_page_title" : MessageLookupByLibrary.simpleMessage("Post comments"), "post__comments_view_all_comments" : m31, + "post__create_camera" : MessageLookupByLibrary.simpleMessage("Camera"), "post__create_hashtags_invalid" : m32, + "post__create_media" : MessageLookupByLibrary.simpleMessage("Media"), "post__create_new" : MessageLookupByLibrary.simpleMessage("New post"), "post__create_new_community_post_label" : MessageLookupByLibrary.simpleMessage("Create new communtiy post"), "post__create_new_post_label" : MessageLookupByLibrary.simpleMessage("Create new post"), @@ -752,7 +757,7 @@ class MessageLookup extends MessageLookupByLibrary { "post__you_shared_with" : MessageLookupByLibrary.simpleMessage("You shared with"), "post_body_link_preview__empty" : MessageLookupByLibrary.simpleMessage("This link could not be previewed"), "post_body_link_preview__error_with_description" : m36, - "post_body_link_preview__invalid" : m67, + "post_body_link_preview__invalid" : m68, "post_body_media__unsupported" : MessageLookupByLibrary.simpleMessage("Unsupported media type"), "post_uploader__cancelled" : MessageLookupByLibrary.simpleMessage("Cancelled!"), "post_uploader__cancelling" : MessageLookupByLibrary.simpleMessage("Cancelling"), @@ -781,6 +786,7 @@ class MessageLookup extends MessageLookupByLibrary { "user__billion_postfix" : MessageLookupByLibrary.simpleMessage("b"), "user__block_description" : MessageLookupByLibrary.simpleMessage("You will both dissapear from each other\'s social network experience, with the exception of communities which the person is a staff member of."), "user__block_user" : MessageLookupByLibrary.simpleMessage("Block user"), + "user__change_email_current_email_text" : MessageLookupByLibrary.simpleMessage("Current email"), "user__change_email_email_text" : MessageLookupByLibrary.simpleMessage("Email"), "user__change_email_error" : MessageLookupByLibrary.simpleMessage("Email is already registered"), "user__change_email_hint_text" : MessageLookupByLibrary.simpleMessage("Enter your new email"), From 97e3e11cadc06775a2e0de2692140480d31af78f Mon Sep 17 00:00:00 2001 From: Komposten Date: Fri, 8 May 2020 15:04:54 +0200 Subject: [PATCH 39/39] :bug: Fix text shares not working --- lib/services/share.dart | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/services/share.dart b/lib/services/share.dart index 55faec0b7..c41a20c30 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -105,9 +105,9 @@ class ShareService { _queuedShare = null; _isProcessingShare = true; - var operation = CancelableOperation.fromFuture(_onShare(share)); - _activeShares[share] = ShareOperation(share, operation); + _activeShares[share] = ShareOperation(share, _onShare); _activeShares[share].then(() => _activeShares.remove(share)); + _activeShares[share].start(); _isProcessingShare = false; // Recurse since a new share might have came in while the last was being processed. @@ -152,11 +152,10 @@ class ShareService { if (share.text != null) { text = share.text; if (!_validationService.isPostTextAllowedLength(text)) { - String errorMessage = _localizationService - .error__receive_share_text_too_long(ValidationService.POST_MAX_LENGTH); - _toastService.error( - message: errorMessage, - context: _context); + String errorMessage = + _localizationService.error__receive_share_text_too_long( + ValidationService.POST_MAX_LENGTH); + _toastService.error(message: errorMessage, context: _context); return; } } @@ -181,6 +180,8 @@ class ShareService { } class ShareOperation { + final Future Function(Share) _shareFunction; + Share share; CancelableOperation shareOperation; CancelableOperation subOperation; @@ -190,7 +191,11 @@ class ShareOperation { bool _subComplete = false; FutureOr Function() _callback; - ShareOperation(this.share, this.shareOperation) { + ShareOperation(this.share, Future Function(Share) shareFunction) + : _shareFunction = shareFunction; + + void start() { + shareOperation = CancelableOperation.fromFuture(_shareFunction(share)); shareOperation.then((_) { _shareComplete = true; _complete();