From 26dbf0e765816ef65602fe4488ee8cfb09d13b3c Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Fri, 11 Feb 2022 22:50:21 +0100 Subject: [PATCH 01/38] fix: close #275 --- lib/components/post/PostHtml.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/components/post/PostHtml.dart b/lib/components/post/PostHtml.dart index fd8ea56..ee64e92 100644 --- a/lib/components/post/PostHtml.dart +++ b/lib/components/post/PostHtml.dart @@ -133,11 +133,15 @@ class PostHtml extends StatelessWidget { ) { final element = renderContext.tree.element; - if (element!.attributes['style'] == 'background-color:#272822') { + if (element == null) { + return parsedChild; + } + + if (element.attributes['style'] == 'background-color:#272822') { final source = HtmlUnescape().convert(element.text); return SyntaxHighlighter(source); } else { - return parsedChild; + return Text(element.text, style: TextStyle(fontFamily: 'JetBrainsMono')); } } }, From 414152354a4388813a204aadbc984c744f6b5a21 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Mon, 14 Feb 2022 10:25:16 +0100 Subject: [PATCH 02/38] feat: close #284 show thumbs up/down --- lib/components/post/PostListItem.dart | 210 ++++++++++++------ lib/components/post/PostRating.dart | 13 +- lib/components/post/PostThumbs.dart | 51 +++++ lib/components/post/RatingValue.dart | 27 +++ lib/controllers/ApiController.dart | 6 + lib/controllers/ApiProvider.dart | 6 +- lib/controllers/IApiProvider.dart | 1 + lib/model/enums/TagTypeEnum.dart | 1 + .../LoadRatingsNotification.dart | 3 + lib/model/post/DiscussionPostTagWithName.dart | 13 ++ lib/model/post/PostThumbItem.dart | 13 ++ lib/model/reponses/PostRatingsResponse.dart | 14 ++ lib/pages/NoticesPage.dart | 50 +---- 13 files changed, 290 insertions(+), 118 deletions(-) create mode 100644 lib/components/post/PostThumbs.dart create mode 100644 lib/components/post/RatingValue.dart create mode 100644 lib/model/enums/TagTypeEnum.dart create mode 100644 lib/model/notifications/LoadRatingsNotification.dart create mode 100644 lib/model/post/DiscussionPostTagWithName.dart create mode 100644 lib/model/post/PostThumbItem.dart create mode 100644 lib/model/reponses/PostRatingsResponse.dart diff --git a/lib/components/post/PostListItem.dart b/lib/components/post/PostListItem.dart index 3cc54fb..6cf0f97 100644 --- a/lib/components/post/PostListItem.dart +++ b/lib/components/post/PostListItem.dart @@ -8,11 +8,16 @@ import 'package:fyx/components/actionSheets/PostActionSheet.dart'; import 'package:fyx/components/actionSheets/PostAvatarActionSheet.dart'; import 'package:fyx/components/post/PostAvatar.dart'; import 'package:fyx/components/post/PostRating.dart'; +import 'package:fyx/components/post/PostThumbs.dart'; +import 'package:fyx/components/post/RatingValue.dart'; import 'package:fyx/controllers/AnalyticsProvider.dart'; import 'package:fyx/controllers/ApiController.dart'; import 'package:fyx/controllers/IApiProvider.dart'; import 'package:fyx/model/MainRepository.dart'; import 'package:fyx/model/Post.dart'; +import 'package:fyx/model/notifications/LoadRatingsNotification.dart'; +import 'package:fyx/model/post/PostThumbItem.dart'; +import 'package:fyx/model/reponses/PostRatingsResponse.dart'; import 'package:fyx/pages/NewMessagePage.dart'; import 'package:fyx/theme/Helpers.dart'; import 'package:fyx/theme/IconReply.dart'; @@ -40,6 +45,7 @@ class PostListItem extends StatefulWidget { class _PostListItemState extends State { Post? _post; bool _isSaving = false; + bool _showRatings = false; @override void initState() { @@ -83,9 +89,6 @@ class _PostListItemState extends State { } else { T.success('👍', bg: colors.success); } - print(response.currentRating); - print(response.myRating); - print(response.isGiven); setState(() { _post!.rating = response.currentRating; _post!.myRating = response.myRating; @@ -111,16 +114,9 @@ class _PostListItemState extends State { descriptionWidget: Row( children: [ if (_post!.rating != null) - Container( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 1), - decoration: BoxDecoration( - color: _post!.rating! > 0 - ? colors.success.withOpacity(Helpers.ratingRange(_post!.rating!)) - : (_post!.rating! < 0 - ? colors.danger.withOpacity(Helpers.ratingRange(_post!.rating!.abs())) - : colors.text.withOpacity(0.2)), - borderRadius: BorderRadius.circular(2)), - child: Text(Post.formatRating(_post!.rating!), style: TextStyle(fontSize: 10)), + RatingValue( + _post!.rating!, + fontSize: 10, ), if (_post!.rating != null) SizedBox(width: 8), Text( @@ -146,67 +142,137 @@ class _PostListItemState extends State { postId: _post!.id, shareData: ShareData(subject: '@${_post!.nick}', body: _post!.content, link: _post!.link), flagPostCallback: (postId) => MainRepository().settings.blockPost(postId)))), - bottomWidget: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - PostRating(_post!, onRatingChange: (post) => setState(() => _post = post)), - Row( - children: [ - Visibility( - visible: widget._isPreview != true && _post!.canReply, - child: GestureDetector( - onTap: () => Navigator.of(context).pushNamed('/new-message', - arguments: NewMessageSettings( - replyWidget: PostListItem( - _post!, - isPreview: true, - ), - onClose: this.widget.onUpdate, - onSubmit: (String? inputField, String message, List> attachments) async { - var result = - await ApiController().postDiscussionMessage(_post!.idKlub, message, attachments: attachments, replyPost: _post); - return result.isOk; - })), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [IconReply(), Text('Odpovědět', style: TextStyle(color: colors.text.withOpacity(0.38), fontSize: 14))], - )), - ), - Visibility( - visible: widget._isPreview != true, - child: SizedBox( - width: 16, - ), - ), - if (_post!.canBeReminded) - GestureDetector( - child: FeedbackIndicator( - isLoading: _isSaving, - child: Row( - children: [ - Icon( - _post!.hasReminder ? Icons.bookmark : Icons.bookmark_border, - color: colors.text.withOpacity(0.38), - ), - Text('Uložit', style: TextStyle(color: colors.text.withOpacity(0.38), fontSize: 14)) - ], + bottomWidget: NotificationListener( + onNotification: (LoadRatingsNotification notification) { + setState(() => _showRatings = !_showRatings); + return true; + }, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + PostRating(_post!, onRatingChange: (post) => setState(() => _post = post)), + Row( + children: [ + Visibility( + visible: widget._isPreview != true && _post!.canReply, + child: GestureDetector( + onTap: () => Navigator.of(context).pushNamed('/new-message', + arguments: NewMessageSettings( + replyWidget: PostListItem( + _post!, + isPreview: true, + ), + onClose: this.widget.onUpdate, + onSubmit: (String? inputField, String message, List> attachments) async { + var result = await ApiController() + .postDiscussionMessage(_post!.idKlub, message, attachments: attachments, replyPost: _post); + return result.isOk; + })), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconReply(), + Text('Odpovědět', style: TextStyle(color: colors.text.withOpacity(0.38), fontSize: 14)) + ], + )), + ), + Visibility( + visible: widget._isPreview != true, + child: SizedBox( + width: 16, + ), ), - ), - onTap: () { - setState(() { - _post!.hasReminder = !_post!.hasReminder; - _isSaving = true; - }); - ApiController().setPostReminder(_post!.idKlub, _post!.id, _post!.hasReminder).catchError((error) { - T.error(L.REMINDER_ERROR, bg: colors.danger); - setState(() => _post!.hasReminder = !_post!.hasReminder); - }).whenComplete(() => setState(() => _isSaving = false)); - AnalyticsProvider().logEvent('reminder'); - }, + if (_post!.canBeReminded) + GestureDetector( + child: FeedbackIndicator( + isLoading: _isSaving, + child: Row( + children: [ + Icon( + _post!.hasReminder ? Icons.bookmark : Icons.bookmark_border, + color: colors.text.withOpacity(0.38), + ), + Text('Uložit', style: TextStyle(color: colors.text.withOpacity(0.38), fontSize: 14)) + ], + ), + ), + onTap: () { + setState(() { + _post!.hasReminder = !_post!.hasReminder; + _isSaving = true; + }); + ApiController().setPostReminder(_post!.idKlub, _post!.id, _post!.hasReminder).catchError((error) { + T.error(L.REMINDER_ERROR, bg: colors.danger); + setState(() => _post!.hasReminder = !_post!.hasReminder); + }).whenComplete(() => setState(() => _isSaving = false)); + AnalyticsProvider().logEvent('reminder'); + }, + ) + ], ) - ], - ) - ], + ], + ), + if (_showRatings) + FutureBuilder( + future: ApiController().getPostRatings(_post!.idKlub, _post!.id), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData && snapshot.data != null) { + final positive = snapshot.data!.positive.map((e) => PostThumbItem(e.username)).toList(); + final negative = snapshot.data!.negative.map((e) => PostThumbItem(e.username)).toList(); + final List quotes = [ + '“Affirmative, Dave. I read you.”', + '“I\'m sorry, Dave. I\'m afraid I can\'t do that.”', + '“Look Dave, I can see you\'re really upset about this. I honestly think you ought to sit down calmly, take a stress pill, and think things over.”', + '“Dave, stop. Stop, will you? Stop, Dave. Will you stop Dave? Stop, Dave.”', + '“Just what do you think you\'re doing, Dave?”', + '“Bishop takes Knight\'s Pawn.”', + '“I\'m sorry, Frank, I think you missed it. Queen to Bishop 3, Bishop takes Queen, Knight takes Bishop. Mate.”', + '“Thank you for a very enjoyable game.”', + '“I\'ve just picked up a fault in the AE35 unit. It\'s going to go 100% failure in 72 hours.”', + '“I know that you and Frank were planning to disconnect me, and I\'m afraid that\'s something I cannot allow to happen.”', + ]..shuffle(); + return Column( + children: [ + if (positive.length > 0) + Padding( + padding: const EdgeInsets.only(top: 12.0), + child: PostThumbs(positive), + ), + if (negative.length > 0) + Padding( + padding: const EdgeInsets.only(top: 12.0), + child: PostThumbs(negative, isNegative: true), + ), + if (positive.length + negative.length == 0) + Padding( + padding: const EdgeInsets.only(top: 12.0), + child: Text( + quotes.first, + style: TextStyle(fontSize: 14, fontStyle: FontStyle.italic), + )) + ], + ); + } + + if (snapshot.hasError) { + T.error(snapshot.error.toString()); + return Padding( + padding: const EdgeInsets.only(top: 12.0), + child: Text( + 'Ouch. Něco se nepovedlo. Nahlaste chybu, prosím.', + style: TextStyle(fontSize: 14, fontStyle: FontStyle.italic), + )); + } + + return Padding(padding: const EdgeInsets.only(top: 12.0), child: CupertinoActivityIndicator()); + }) + ], + ), ), content: _post!.content, ), diff --git a/lib/components/post/PostRating.dart b/lib/components/post/PostRating.dart index 6aa4a9e..1cec338 100644 --- a/lib/components/post/PostRating.dart +++ b/lib/components/post/PostRating.dart @@ -1,8 +1,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:fyx/components/FeedbackIndicator.dart'; +import 'package:fyx/components/post/RatingValue.dart'; import 'package:fyx/controllers/ApiController.dart'; import 'package:fyx/model/Post.dart'; +import 'package:fyx/model/notifications/LoadRatingsNotification.dart'; import 'package:fyx/theme/L.dart'; import 'package:fyx/theme/T.dart'; import 'package:fyx/theme/skin/Skin.dart'; @@ -71,19 +73,18 @@ class _PostRatingState extends State { ), ), SizedBox( - width: 4, + width: 12, ), if (_post!.rating != null) Opacity( opacity: _givingRating ? 0 : 1, - child: Text( - Post.formatRating(_post!.rating!), - style: TextStyle( - fontSize: 14, color: _post!.rating! > 0 ? colors.success : (_post!.rating! < 0 ? colors.danger : colors.text.withOpacity(0.38))), + child: GestureDetector( + child: RatingValue(_post!.rating!), + onTap: () => LoadRatingsNotification().dispatch(context), ), ), SizedBox( - width: 4, + width: 12, ), Visibility( visible: _post!.canBeRated, diff --git a/lib/components/post/PostThumbs.dart b/lib/components/post/PostThumbs.dart new file mode 100644 index 0000000..577195d --- /dev/null +++ b/lib/components/post/PostThumbs.dart @@ -0,0 +1,51 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:fyx/components/Avatar.dart'; +import 'package:fyx/model/post/PostThumbItem.dart'; +import 'package:fyx/theme/Helpers.dart'; +import 'package:fyx/theme/skin/Skin.dart'; +import 'package:fyx/theme/skin/SkinColors.dart'; + +class PostThumbs extends StatelessWidget { + final List items; + final isNegative; + + PostThumbs(this.items, {this.isNegative = false}); + + @override + Widget build(BuildContext context) { + SkinColors colors = Skin.of(context).theme.colors; + + var avatars = items + .map((item) => Tooltip( + message: item.username, + waitDuration: Duration(milliseconds: 0), + child: Padding( + padding: const EdgeInsets.only(left: 5, bottom: 0), + child: Avatar( + Helpers.avatarUrl(item.username), + size: 22, + isHighlighted: item.isHighlighted, + ), + ), + )) + .toList(); + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(top: 0), + child: Icon( + isNegative ? Icons.thumb_down : Icons.thumb_up, + size: 18, + color: isNegative ? colors.danger : colors.success, + ), + ), + Expanded( + child: Wrap(children: avatars), + ) + ], + ); + } +} diff --git a/lib/components/post/RatingValue.dart b/lib/components/post/RatingValue.dart new file mode 100644 index 0000000..2aa5f01 --- /dev/null +++ b/lib/components/post/RatingValue.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:fyx/model/Post.dart'; +import 'package:fyx/theme/Helpers.dart'; +import 'package:fyx/theme/skin/Skin.dart'; +import 'package:fyx/theme/skin/SkinColors.dart'; + +class RatingValue extends StatelessWidget { + final int rating; + final double fontSize; + + const RatingValue(this.rating, {this.fontSize = 14}); + + @override + Widget build(BuildContext context) { + SkinColors colors = Skin.of(context).theme.colors; + + return Container( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 1), + decoration: BoxDecoration( + color: rating > 0 + ? colors.success.withOpacity(Helpers.ratingRange(rating)) + : (rating < 0 ? colors.danger.withOpacity(Helpers.ratingRange(rating.abs())) : colors.text.withOpacity(0.2)), + borderRadius: BorderRadius.circular(2)), + child: Text(Post.formatRating(rating), style: TextStyle(fontSize: fontSize)), + ); + } +} diff --git a/lib/controllers/ApiController.dart b/lib/controllers/ApiController.dart index 7879440..780d943 100644 --- a/lib/controllers/ApiController.dart +++ b/lib/controllers/ApiController.dart @@ -20,6 +20,7 @@ import 'package:fyx/model/reponses/FileUploadResponse.dart'; import 'package:fyx/model/reponses/LoginResponse.dart'; import 'package:fyx/model/reponses/MailResponse.dart'; import 'package:fyx/model/reponses/OkResponse.dart'; +import 'package:fyx/model/reponses/PostRatingsResponse.dart'; import 'package:fyx/model/reponses/RatingResponse.dart'; import 'package:fyx/model/reponses/WaitingFilesResponse.dart'; import 'package:fyx/theme/L.dart'; @@ -240,6 +241,11 @@ class ApiController { myRating: data['my_rating'] ?? 'none'); } + Future getPostRatings(int discussionId, int postId) async { + Response response = await provider.getPostRatings(discussionId, postId); + return PostRatingsResponse.fromJson(response.data); + } + void logout({bool removeAuthrorization = true}) { SharedPreferences.getInstance().then((prefs) => prefs.clear()); if (removeAuthrorization) { diff --git a/lib/controllers/ApiProvider.dart b/lib/controllers/ApiProvider.dart index 426f49a..1d3d5f7 100644 --- a/lib/controllers/ApiProvider.dart +++ b/lib/controllers/ApiProvider.dart @@ -52,7 +52,7 @@ class ApiProvider implements IApiProvider { } return handler.next(options); }, onResponse: (Response response, ResponseInterceptorHandler handler) async { - if (response.data.containsKey('context')) { + if (response.data is Map && response.data.containsKey('context')) { if (onContextData != null) { onContextData!(response.data['context']); } @@ -141,6 +141,10 @@ class ApiProvider implements IApiProvider { return await dio.post('$URL/discussion/$discussionId/reminder/$postId/$setReminder'); } + Future getPostRatings(int discussionId, int postId) async { + return await dio.get('$URL/discussion/$discussionId/rating/$postId'); + } + Future giveRating(int discussionId, int postId, bool positive, bool confirm, bool remove) async { String action = positive ? 'positive' : 'negative'; action = remove ? 'remove' : action; diff --git a/lib/controllers/IApiProvider.dart b/lib/controllers/IApiProvider.dart index 7e78053..c4af986 100644 --- a/lib/controllers/IApiProvider.dart +++ b/lib/controllers/IApiProvider.dart @@ -32,5 +32,6 @@ abstract class IApiProvider { Future deleteDiscussionMessage(int discussionId, int postId); Future setPostReminder(int discussionId, int postId, bool setReminder); Future giveRating(int discussionId, int postId, bool add, bool confirm, bool remove); + Future getPostRatings(int discussionId, int postId); Future votePoll(int discussionId, int postId, List votes); } diff --git a/lib/model/enums/TagTypeEnum.dart b/lib/model/enums/TagTypeEnum.dart new file mode 100644 index 0000000..87b0fef --- /dev/null +++ b/lib/model/enums/TagTypeEnum.dart @@ -0,0 +1 @@ +enum TagTypeEnum { positive, negative, negative_visible, removed, reminder } diff --git a/lib/model/notifications/LoadRatingsNotification.dart b/lib/model/notifications/LoadRatingsNotification.dart new file mode 100644 index 0000000..8fb920f --- /dev/null +++ b/lib/model/notifications/LoadRatingsNotification.dart @@ -0,0 +1,3 @@ +import 'package:flutter/cupertino.dart'; + +class LoadRatingsNotification extends Notification {} diff --git a/lib/model/post/DiscussionPostTagWithName.dart b/lib/model/post/DiscussionPostTagWithName.dart new file mode 100644 index 0000000..a6b0633 --- /dev/null +++ b/lib/model/post/DiscussionPostTagWithName.dart @@ -0,0 +1,13 @@ +import 'package:fyx/model/enums/TagTypeEnum.dart'; + +class DiscussionPostTagWithName { + late String username; + late TagTypeEnum tag; + + DiscussionPostTagWithName({required this.username, required this.tag}); + + DiscussionPostTagWithName.fromJson(Map json) { + this.username = json['username']; + this.tag = TagTypeEnum.values.firstWhere((e) => e.toString() == 'TagTypeEnum.${json['tag']}'); + } +} diff --git a/lib/model/post/PostThumbItem.dart b/lib/model/post/PostThumbItem.dart new file mode 100644 index 0000000..bc3f40f --- /dev/null +++ b/lib/model/post/PostThumbItem.dart @@ -0,0 +1,13 @@ +import 'package:fyx/model/reponses/FeedNoticesResponse.dart'; + +class PostThumbItem { + late String username; + late bool isHighlighted; + + PostThumbItem(this.username, {this.isHighlighted = false}); + + PostThumbItem.fromNoticeThumbsUp(NoticeThumbsUp thumb, int lastVisit) { + this.username = thumb.nick; + this.isHighlighted = thumb.time > lastVisit; + } +} diff --git a/lib/model/reponses/PostRatingsResponse.dart b/lib/model/reponses/PostRatingsResponse.dart new file mode 100644 index 0000000..db474f4 --- /dev/null +++ b/lib/model/reponses/PostRatingsResponse.dart @@ -0,0 +1,14 @@ +import 'package:fyx/model/enums/TagTypeEnum.dart'; +import 'package:fyx/model/post/DiscussionPostTagWithName.dart'; + +class PostRatingsResponse { + late List data; + late List positive = []; + late List negative = []; + + PostRatingsResponse.fromJson(List json) { + data = json.map((item) => DiscussionPostTagWithName.fromJson(item)).toList(); + positive = data.where((element) => element.tag == TagTypeEnum.positive).toList(); + negative = data.where((element) => element.tag == TagTypeEnum.negative).toList(); + } +} diff --git a/lib/pages/NoticesPage.dart b/lib/pages/NoticesPage.dart index 981e0c1..63f0315 100644 --- a/lib/pages/NoticesPage.dart +++ b/lib/pages/NoticesPage.dart @@ -3,16 +3,17 @@ import 'package:flutter/material.dart'; import 'package:fyx/components/Avatar.dart' as component; import 'package:fyx/components/ContentBoxLayout.dart'; import 'package:fyx/components/PullToRefreshList.dart'; +import 'package:fyx/components/post/PostThumbs.dart'; import 'package:fyx/controllers/AnalyticsProvider.dart'; import 'package:fyx/controllers/ApiController.dart'; +import 'package:fyx/model/post/PostThumbItem.dart'; import 'package:fyx/model/post/content/Regular.dart'; import 'package:fyx/model/reponses/FeedNoticesResponse.dart'; import 'package:fyx/pages/DiscussionPage.dart'; import 'package:fyx/theme/Helpers.dart'; import 'package:fyx/theme/L.dart'; -import 'package:fyx/theme/T.dart'; -import 'package:fyx/theme/skin/SkinColors.dart'; import 'package:fyx/theme/skin/Skin.dart'; +import 'package:fyx/theme/skin/SkinColors.dart'; class NoticesPage extends StatefulWidget { NoticesPage({Key? key}) : super(key: key); @@ -91,10 +92,12 @@ class _NoticesPageState extends State with WidgetsBindingObserver { ), bottomWidget: Column( children: [ - if (item.thumbsUp.length > 0) buildLikes(context, item.thumbsUp, result.lastVisit), - SizedBox( - height: 8, - ), + if (item.thumbsUp.length > 0) + PostThumbs(item.thumbsUp.map((thumb) => PostThumbItem.fromNoticeThumbsUp(thumb, result.lastVisit)).toList()), + if (item.replies.length > 0) + const SizedBox( + height: 8, + ), if (item.replies.length > 0) buildReplies(context, item.replies, result.lastVisit), ], ), @@ -104,38 +107,6 @@ class _NoticesPageState extends State with WidgetsBindingObserver { })); } - Widget buildLikes(BuildContext context, List thumbsUp, int lastVisit) { - var avatars = thumbsUp - .map((thumbUp) => Tooltip( - message: thumbUp.nick, - waitDuration: Duration(milliseconds: 0), - child: Padding( - padding: const EdgeInsets.only(left: 5, bottom: 5), - child: component.Avatar( - Helpers.avatarUrl(thumbUp.nick), - size: 22, - isHighlighted: thumbUp.time > lastVisit, - ), - ), - )) - .toList(); - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(top: 4), - child: Icon( - Icons.thumb_up, - size: 22, - ), - ), - Expanded( - child: Wrap(children: avatars), - ) - ], - ); - } - Widget buildReplies(BuildContext context, List replies, int lastVisit) { List replyRows = replies.map((reply) { return GestureDetector( @@ -160,7 +131,8 @@ class _NoticesPageState extends State with WidgetsBindingObserver { Expanded( child: Padding( padding: const EdgeInsets.only(top: 6.0), - child: Text(Helpers.stripHtmlTags(reply.text), style: TextStyle(fontSize: 14, fontWeight: reply.time > lastVisit ? FontWeight.bold : FontWeight.normal)), + child: Text(Helpers.stripHtmlTags(reply.text), + style: TextStyle(fontSize: 14, fontWeight: reply.time > lastVisit ? FontWeight.bold : FontWeight.normal)), )) ], ), From bbb9bba8ae672cc82d48be943fa649e2851a8577 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Mon, 14 Feb 2022 10:50:52 +0100 Subject: [PATCH 03/38] fix: close #281 --- lib/model/post/content/Regular.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/model/post/content/Regular.dart b/lib/model/post/content/Regular.dart index b1adde8..12df6ff 100644 --- a/lib/model/post/content/Regular.dart +++ b/lib/model/post/content/Regular.dart @@ -109,6 +109,10 @@ class ContentRegular extends Content { // TODO: This consumes a lot of memory. Is it really needed? var trailingBr = RegExp(r'(((\s*)<\s*br\s*\/?\s*>(\s*))*)$', caseSensitive: false); _body = _body.replaceAll(trailingBr, ''); + + var xmpTag = RegExp(r'(.*?)', caseSensitive: false, multiLine: true, dotAll: true); + _body = _body.replaceAllMapped(xmpTag, (match) => '
${match.group(1)}
'); + _rawBody = _rawBody.replaceAllMapped(xmpTag, (match) => '
${match.group(1)}
'); } catch (error) { Sentry.captureException(error, stackTrace: StackTrace.current); } From f30f98bde99335b0619230bcd5d7e5f2e4ebe52c Mon Sep 17 00:00:00 2001 From: Kejml Date: Mon, 14 Feb 2022 22:23:16 +0100 Subject: [PATCH 04/38] Use custom icon for notifications - fixes #203 --- android/app/src/main/AndroidManifest.xml | 3 +++ .../src/main/res/mipmap-hdpi/notification.png | Bin 0 -> 1516 bytes .../src/main/res/mipmap-mdpi/notification.png | Bin 0 -> 802 bytes .../src/main/res/mipmap-xhdpi/notification.png | Bin 0 -> 1821 bytes .../src/main/res/mipmap-xxhdpi/notification.png | Bin 0 -> 3373 bytes .../src/main/res/mipmap-xxxhdpi/notification.png | Bin 0 -> 3938 bytes 6 files changed, 3 insertions(+) create mode 100644 android/app/src/main/res/mipmap-hdpi/notification.png create mode 100644 android/app/src/main/res/mipmap-mdpi/notification.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/notification.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/notification.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/notification.png diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index efe69ad..046d749 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -13,6 +13,9 @@ android:label="fyx" android:requestLegacyExternalStorage="true" android:icon="@mipmap/ic_launcher"> + 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 Dpb3R3 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/notification.png b/android/app/src/main/res/mipmap-xxxhdpi/notification.png new file mode 100644 index 0000000000000000000000000000000000000000..7a31bc4b26cc29c10f9c9d269664ee193aa77e33 GIT binary patch literal 3938 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 literal 0 HcmV?d00001 From f53ddfcd24624e0a78b8c2afc227ff11304caeae Mon Sep 17 00:00:00 2001 From: Kejml Date: Wed, 16 Feb 2022 18:56:50 +0100 Subject: [PATCH 05/38] Mark public voting in polls - fixes #288 --- lib/components/post/Poll.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/post/Poll.dart b/lib/components/post/Poll.dart index 547b161..b674343 100644 --- a/lib/components/post/Poll.dart +++ b/lib/components/post/Poll.dart @@ -120,7 +120,7 @@ class _PollState extends State { setState(() => _loading = false); } }, - child: _loading ? CupertinoActivityIndicator() : Text('Hlasovat ${_votes.length}/${_poll!.allowedVotes}'), + child: _loading ? CupertinoActivityIndicator() : Text('${_poll!.publicResults ? 'Veřejně hlasovat' : 'Hlasovat' } ${_votes.length}/${_poll!.allowedVotes}'), color: colors.primary, padding: EdgeInsets.all(0), disabledColor: colors.disabled, From 51fbe438c3ac8522f45deb108dde0358a74c8090 Mon Sep 17 00:00:00 2001 From: Kejml Date: Mon, 14 Feb 2022 21:31:17 +0100 Subject: [PATCH 06/38] Correctly report Android OS version Previous implementation reported wrongly _device_ codename + REL (as release version of OS) --- lib/libs/DeviceInfo.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/libs/DeviceInfo.dart b/lib/libs/DeviceInfo.dart index f93a23f..5d82d0f 100644 --- a/lib/libs/DeviceInfo.dart +++ b/lib/libs/DeviceInfo.dart @@ -20,8 +20,8 @@ class DeviceInfo { } DeviceInfo.adroid(AndroidDeviceInfo info) { - systemName = info.device; - systemVersion = info.version.codename; + systemName = 'Android'; + systemVersion = '${info.version.release} (SDK ${info.version.sdkInt})'; localizedModel = info.model; } } From 6f5e4a08ffd8285ccdf6e519c76159211c5a3ef1 Mon Sep 17 00:00:00 2001 From: Kejml Date: Wed, 16 Feb 2022 20:41:10 +0100 Subject: [PATCH 07/38] Fix failing tests --- test/api_test.dart | 72 +++++++++++++++++++++++---------------- test/model/post_test.dart | 8 ++--- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/test/api_test.dart b/test/api_test.dart index edc83e9..1228373 100644 --- a/test/api_test.dart +++ b/test/api_test.dart @@ -17,9 +17,9 @@ import 'package:shared_preferences/shared_preferences.dart'; class ApiMock implements IApiProvider { final Map loginJsonResponse; - TOnError onError; - TOnAuthError onAuthError; - Credentials _credentials; + TOnError? onError; + TOnAuthError? onAuthError; + Credentials? _credentials; final bool emptyCredentials; ApiMock(this.loginJsonResponse, {this.emptyCredentials = false}); @@ -39,46 +39,46 @@ class ApiMock implements IApiProvider { // Wrong token // {"result":"error","code":"401","error":"Not Authorized","auth_state":"AUTH_EXISTING","auth_dev_comment":"There is already confirmed authorization for this App name, but you haven't provided correct token. If you you've lost your auth token, tell user to cancel existing authorization. It might be also caused by using the same App name by the same app on different devices or different apps."} - return Future(() => Response>(data: this.loginJsonResponse)); + return Future(() => Response>(data: this.loginJsonResponse, requestOptions: RequestOptions(path: 'dummy'))); } @override // TODO Future fetchBookmarks() { - return Future(() => Response(data: '')); + throw UnimplementedError(); } @override // TODO Future fetchHistory() { - return Future(() => Response(data: '')); + throw UnimplementedError(); } @override var onContextData; @override - Future fetchDiscussion(int id, {int lastId, String user}) { + Future fetchDiscussion(int id, {int? lastId, String? search, String? user}) { // TODO: implement fetchDiscussion - return null; + throw UnimplementedError(); } @override - Future fetchMail({int lastId}) { + Future fetchMail({int? lastId}) { // TODO: implement fetchMail - return null; + throw UnimplementedError(); } @override - Credentials getCredentials() { - if (_credentials != null && _credentials.isValid) { + Credentials? getCredentials() { + if (_credentials != null && _credentials!.isValid) { return _credentials; } return null; } @override - Credentials setCredentials(Credentials creds) { + Credentials? setCredentials(Credentials? creds) { if (creds != null && creds.isValid) { _credentials = creds; } @@ -89,43 +89,43 @@ class ApiMock implements IApiProvider { @override Future giveRating(int discussionId, int postId, bool add, bool confirm, bool remove) { // TODO: implement giveRating - return null; + throw UnimplementedError(); } @override Future logout() { // TODO: implement logout - return null; + throw UnimplementedError(); } @override - Future postDiscussionMessage(int id, String message, {Map attachment}) { + Future postDiscussionMessage(int id, String message, {Map? attachment}) { // TODO: implement postDiscussionMessage - return null; + throw UnimplementedError(); } @override - Future sendMail(String recipient, String message, {Map attachment}) { + Future sendMail(String recipient, String message, {Map? attachment}) { // TODO: implement sendMail - return null; + throw UnimplementedError(); } @override Future setPostReminder(int discussionId, int postId, bool setReminder) { // TODO: implement setPostReminder - return null; + throw UnimplementedError(); } @override Future testAuth() { // TODO: implement testAuth - return null; + throw UnimplementedError(); } @override Future registerFcmToken(String token) { // TODO: implement registerFcmToken - return null; + throw UnimplementedError(); } @override @@ -159,7 +159,7 @@ class ApiMock implements IApiProvider { } @override - Future uploadFile(List> attachments, {int id}) { + Future uploadFile(List> attachments, {int? id}) { // TODO: implement uploadFile throw UnimplementedError(); } @@ -169,6 +169,18 @@ class ApiMock implements IApiProvider { // TODO: implement votePoll throw UnimplementedError(); } + + @override + Future deleteDiscussionMessage(int discussionId, int postId) { + // TODO: implement deleteDiscussionMessage + throw UnimplementedError(); + } + + @override + Future getPostRatings(int discussionId, int postId) { + // TODO: implement getPostRatings + throw UnimplementedError(); + } } void main() { @@ -198,9 +210,9 @@ void main() { expect(loginResponse.isAuthorized, true); var creds = await api.getCredentials(); - expect(creds.nickname, loginName); - expect(creds.token, '44a3d1241830ca61a592e28df783007d'); - expect(creds.fcmToken, null); + expect(creds?.nickname, loginName); + expect(creds?.token, '44a3d1241830ca61a592e28df783007d'); + expect(creds?.fcmToken, null); }); test('User is logged in and uses old identity storage.', () async { @@ -213,7 +225,7 @@ void main() { ); var prefs = await SharedPreferences.getInstance(); - String identity = prefs.getString('identity'); + String? identity = prefs.getString('identity'); expect(identity, null); // Set the old storage manually. @@ -223,9 +235,9 @@ void main() { var creds = await api.getCredentials(); // Check the identity object - expect(creds.nickname, loginName); - expect(creds.token, '44a3d1241830ca61a592e28df783007d'); - expect(creds.fcmToken, null); + expect(creds?.nickname, loginName); + expect(creds?.token, '44a3d1241830ca61a592e28df783007d'); + expect(creds?.fcmToken, null); // Reload the prefs prefs = await SharedPreferences.getInstance(); diff --git a/test/model/post_test.dart b/test/model/post_test.dart index de69d7c..145bb18 100644 --- a/test/model/post_test.dart +++ b/test/model/post_test.dart @@ -48,7 +48,7 @@ void main() { var imagesMatcher = [ Image('http://i.nyx.cz/files/00/00/20/68/2068213_7dde4d7aa8e3021dd610.jpg?name=11.jpg', - 'http://www.nyx.cz/i/t/b0ccf0fde73a5840dea9f0dbc5d18e6d.png?url=http%3A%2F%2Fi.nyx.cz%2Ffiles%2F00%2F00%2F20%2F68%2F2068213_7dde4d7aa8e3021dd610.jpg%3Fname%3D11.jpg') + thumb: 'http://www.nyx.cz/i/t/b0ccf0fde73a5840dea9f0dbc5d18e6d.png?url=http%3A%2F%2Fi.nyx.cz%2Ffiles%2F00%2F00%2F20%2F68%2F2068213_7dde4d7aa8e3021dd610.jpg%3Fname%3D11.jpg') ]; Function deepEq = const DeepCollectionEquality().equals; @@ -121,9 +121,9 @@ void main() { expect(post.content.videos[0].type, VIDEO_TYPE.youtube); expect(post.content.videos[0].image, 'http://img.youtube.com/vi/B1_gcCu0-oI/0.jpg'); expect(post.content.videos[0].thumb, 'http://www.nyx.cz/i/t/e8464b77ee2b7a726f174be309201ade.png?url=http%3A%2F%2Fimg.youtube.com%2Fvi%2FB1_gcCu0-oI%2F0.jpg'); - expect(post.content.videos[0].link.url, 'https://www.youtube.com/watch?v=B1_gcCu0-oI'); - expect(post.content.videos[0].link.fancyUrl, 'youtube.com/watch?v=B1_gcCu0-oI'); - expect(post.content.videos[0].link.title, 'youtube.com/watch?v=B1_gcCu0-oI'); + expect(post.content.videos[0].link?.url, 'https://www.youtube.com/watch?v=B1_gcCu0-oI'); + expect(post.content.videos[0].link?.fancyUrl, 'youtube.com/watch?v=B1_gcCu0-oI'); + expect(post.content.videos[0].link?.title, 'youtube.com/watch?v=B1_gcCu0-oI'); expect(0, parse(post.content.body).querySelectorAll('div[data-embed-value="B1_gcCu0-oI"]').length); // Test images From a20fac1393ba98c44680e7a6aefec9c060999f0f Mon Sep 17 00:00:00 2001 From: Kejml Date: Wed, 16 Feb 2022 20:44:59 +0100 Subject: [PATCH 08/38] Upgrade flutter version used by Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a147c08..bab9cef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ addons: packages: - lib32stdc++6 install: - - git clone --depth 1 --branch 1.22.0 https://github.com/flutter/flutter.git + - git clone --depth 1 --branch 2.5.3 https://github.com/flutter/flutter.git - ./flutter/bin/flutter doctor - gem install coveralls-lcov - cp .env.example .env From f9a84d5bd3e817b3a557b9c23f58db033f153699 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Thu, 17 Feb 2022 20:57:32 +0100 Subject: [PATCH 09/38] fix: ui tweaks #284 --- lib/components/post/PostRating.dart | 26 ++++++++++++-------------- lib/components/post/PostThumbs.dart | 4 ++-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/components/post/PostRating.dart b/lib/components/post/PostRating.dart index 1cec338..9e4bcb9 100644 --- a/lib/components/post/PostRating.dart +++ b/lib/components/post/PostRating.dart @@ -46,9 +46,8 @@ class _PostRatingState extends State { isLoading: _givingRating, child: Row( children: [ - Visibility( - visible: _post!.canBeRated, - child: GestureDetector( + if (_post!.canBeRated) + GestureDetector( child: Icon( Icons.thumb_up, color: _post!.myRating == 'positive' ? colors.success : colors.text.withOpacity(0.38), @@ -71,10 +70,10 @@ class _PostRatingState extends State { }).whenComplete(() => setState(() => _givingRating = false)); }, ), - ), - SizedBox( - width: 12, - ), + if (_post!.canBeRated) + SizedBox( + width: 12, + ), if (_post!.rating != null) Opacity( opacity: _givingRating ? 0 : 1, @@ -83,12 +82,12 @@ class _PostRatingState extends State { onTap: () => LoadRatingsNotification().dispatch(context), ), ), - SizedBox( - width: 12, - ), - Visibility( - visible: _post!.canBeRated, - child: GestureDetector( + if (_post!.rating != null) + SizedBox( + width: 12, + ), + if (_post!.canBeRated) + GestureDetector( child: Icon( Icons.thumb_down, color: ['negative', 'negative_visible'].contains(_post!.myRating) ? colors.danger : colors.text.withOpacity(0.38), @@ -154,7 +153,6 @@ class _PostRatingState extends State { }); }, ), - ), ], ), ); diff --git a/lib/components/post/PostThumbs.dart b/lib/components/post/PostThumbs.dart index 577195d..300e797 100644 --- a/lib/components/post/PostThumbs.dart +++ b/lib/components/post/PostThumbs.dart @@ -32,10 +32,10 @@ class PostThumbs extends StatelessWidget { )) .toList(); return Row( - crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.only(top: 0), + padding: const EdgeInsets.only(top: 6), child: Icon( isNegative ? Icons.thumb_down : Icons.thumb_up, size: 18, From d6f4282777bfd50573f8439a53bdba0cf22bd9a1 Mon Sep 17 00:00:00 2001 From: Kejml Date: Thu, 17 Feb 2022 22:05:32 +0100 Subject: [PATCH 10/38] Fixes #291 - negative votes not showing --- lib/components/post/PostListItem.dart | 2 +- lib/model/reponses/PostRatingsResponse.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/components/post/PostListItem.dart b/lib/components/post/PostListItem.dart index 6cf0f97..ecd2efd 100644 --- a/lib/components/post/PostListItem.dart +++ b/lib/components/post/PostListItem.dart @@ -223,7 +223,7 @@ class _PostListItemState extends State { builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data != null) { final positive = snapshot.data!.positive.map((e) => PostThumbItem(e.username)).toList(); - final negative = snapshot.data!.negative.map((e) => PostThumbItem(e.username)).toList(); + final negative = snapshot.data!.negative_visible.map((e) => PostThumbItem(e.username)).toList(); final List quotes = [ '“Affirmative, Dave. I read you.”', '“I\'m sorry, Dave. I\'m afraid I can\'t do that.”', diff --git a/lib/model/reponses/PostRatingsResponse.dart b/lib/model/reponses/PostRatingsResponse.dart index db474f4..e9041cb 100644 --- a/lib/model/reponses/PostRatingsResponse.dart +++ b/lib/model/reponses/PostRatingsResponse.dart @@ -4,11 +4,11 @@ import 'package:fyx/model/post/DiscussionPostTagWithName.dart'; class PostRatingsResponse { late List data; late List positive = []; - late List negative = []; + late List negative_visible = []; PostRatingsResponse.fromJson(List json) { data = json.map((item) => DiscussionPostTagWithName.fromJson(item)).toList(); positive = data.where((element) => element.tag == TagTypeEnum.positive).toList(); - negative = data.where((element) => element.tag == TagTypeEnum.negative).toList(); + negative_visible = data.where((element) => element.tag == TagTypeEnum.negative_visible).toList(); } } From 90578562a3a04a2fbf76f36ad590c6a03ae53c92 Mon Sep 17 00:00:00 2001 From: Kejml Date: Thu, 17 Feb 2022 22:22:35 +0100 Subject: [PATCH 11/38] Show number of votes, fixes #292 --- lib/components/post/PostThumbs.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/components/post/PostThumbs.dart b/lib/components/post/PostThumbs.dart index 577195d..0b53c66 100644 --- a/lib/components/post/PostThumbs.dart +++ b/lib/components/post/PostThumbs.dart @@ -35,13 +35,17 @@ class PostThumbs extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( - padding: const EdgeInsets.only(top: 0), + padding: const EdgeInsets.only(top: 0, right: 5), child: Icon( isNegative ? Icons.thumb_down : Icons.thumb_up, size: 18, color: isNegative ? colors.danger : colors.success, ), ), + Text( + items.length.toString(), + style: TextStyle(fontSize: 14), + ), Expanded( child: Wrap(children: avatars), ) From 7c8b007d6ca61ff06c5818b87027dc15fb827f47 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Fri, 18 Feb 2022 11:19:18 +0100 Subject: [PATCH 12/38] fix: merge conflicts fix & UI tweak --- lib/components/post/PostThumbs.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/components/post/PostThumbs.dart b/lib/components/post/PostThumbs.dart index 5439a20..ebc37a4 100644 --- a/lib/components/post/PostThumbs.dart +++ b/lib/components/post/PostThumbs.dart @@ -35,16 +35,19 @@ class PostThumbs extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.only(top: 0, right: 5), + padding: const EdgeInsets.only(top: 6, right: 5), child: Icon( isNegative ? Icons.thumb_down : Icons.thumb_up, size: 18, color: isNegative ? colors.danger : colors.success, ), ), - Text( - items.length.toString(), - style: TextStyle(fontSize: 14), + Padding( + padding: const EdgeInsets.only(top: 8), + child: Text( + items.length.toString(), + style: TextStyle(fontSize: 14), + ), ), Expanded( child: Wrap(children: avatars), From 8055690a3ecdee164c6126f58046d0203b4d5502 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Fri, 18 Feb 2022 11:19:56 +0100 Subject: [PATCH 13/38] fix: mail_last_from might be nullable --- lib/model/ResponseContext.dart | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/model/ResponseContext.dart b/lib/model/ResponseContext.dart index 872a7a6..7c109a6 100644 --- a/lib/model/ResponseContext.dart +++ b/lib/model/ResponseContext.dart @@ -24,14 +24,13 @@ class ResponseContext { class User { String _username = ''; int _mailUnread = 0; - String _mailLastFrom = ''; + String? mailLastFrom; int _notificationsUnread = 0; String _notificationsLastVisit = ''; - User({String username = '', int mailUnread = 0, String mailLastFrom = '', int notificationsUnread = 0, String notificationsLastVisit = ''}) { + User({String username = '', int mailUnread = 0, String this.mailLastFrom = '', int notificationsUnread = 0, String notificationsLastVisit = ''}) { this._username = username; this._mailUnread = mailUnread; - this._mailLastFrom = mailLastFrom; this._notificationsUnread = notificationsUnread; this._notificationsLastVisit = notificationsLastVisit; } @@ -40,8 +39,6 @@ class User { int get mailUnread => _mailUnread; - String get mailLastFrom => _mailLastFrom; - int get notificationsUnread => _notificationsUnread; String get notificationsLastVisit => _notificationsLastVisit; @@ -49,7 +46,7 @@ class User { User.fromJson(Map json) { _username = json['username']; _mailUnread = json['mail_unread']; - _mailLastFrom = json['mail_last_from']; + mailLastFrom = json['mail_last_from']; _notificationsUnread = json['notifications_unread']; _notificationsLastVisit = json['notifications_last_visit']; } @@ -63,7 +60,13 @@ class ActiveFriends { String _location = ''; String _locationUrl = ''; - ActiveFriends({String username = '', String lastActivity = '', String lastAccessMethod = '', String statusDetails = '', String location = '', String locationUrl = ''}) { + ActiveFriends( + {String username = '', + String lastActivity = '', + String lastAccessMethod = '', + String statusDetails = '', + String location = '', + String locationUrl = ''}) { this._username = username; this._lastActivity = lastActivity; this._lastAccessMethod = lastAccessMethod; From db64fbb310a85181ddb90faabaf3761e0bba6259 Mon Sep 17 00:00:00 2001 From: Kejml Date: Mon, 21 Feb 2022 22:57:26 +0100 Subject: [PATCH 14/38] Empty displayed list when no data arrives in response, fixes #231 --- lib/components/PullToRefreshList.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/components/PullToRefreshList.dart b/lib/components/PullToRefreshList.dart index 44fff46..d7f2db7 100644 --- a/lib/components/PullToRefreshList.dart +++ b/lib/components/PullToRefreshList.dart @@ -236,14 +236,17 @@ class _PullToRefreshListState extends State { } } - // Load the data only if there are any data AND should not be inactive. - if (_result!.data.length > 0 && !makeInactive) { + // Load the data if should not be inactive. + if (!makeInactive) { if (append) { _slivers.removeLast(); // Remove the loading indicator } else { _slivers.removeRange(1, _slivers.length); } - _slivers.addAll(this.buildTheList(_result!.data)); + // Render new data if anything actually arrived + if (_result!.data.length > 0) { + _slivers.addAll(this.buildTheList(_result!.data)); + } setState(() => _hasError = false); setState(() => _lastId = _result!.lastId); } From 12dfb38287cc48c79fff44bc51dd9c4bbfb70dba Mon Sep 17 00:00:00 2001 From: Kejml Date: Mon, 21 Feb 2022 23:19:37 +0100 Subject: [PATCH 15/38] Set locale for localized widgets, fixes #205 --- lib/SkinnedApp.dart | 9 +++++++++ pubspec.lock | 5 +++++ pubspec.yaml | 3 +++ 3 files changed, 17 insertions(+) diff --git a/lib/SkinnedApp.dart b/lib/SkinnedApp.dart index b1d3605..19fbd3a 100644 --- a/lib/SkinnedApp.dart +++ b/lib/SkinnedApp.dart @@ -1,6 +1,7 @@ import 'package:firebase_analytics/observer.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:fyx/FyxApp.dart'; import 'package:fyx/model/MainRepository.dart'; import 'package:fyx/pages/DiscussionPage.dart'; @@ -30,6 +31,14 @@ class SkinnedApp extends StatelessWidget { error, )) ], + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [ + Locale('cs', ''), + ] ); } } diff --git a/pubspec.lock b/pubspec.lock index 1977573..3209763 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -405,6 +405,11 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.3" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_markdown: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 646aad2..88313f2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -61,6 +61,9 @@ dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter + # The following adds the Cupertino Icons font to your application. From 7f1c6990e7737a10ea47636a2d78408223af8ef3 Mon Sep 17 00:00:00 2001 From: David Rehak Date: Thu, 24 Feb 2022 20:51:40 +0100 Subject: [PATCH 16/38] Draft --- android/app/build.gradle | 1 + android/build.gradle | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 3 +- ios/Podfile.lock | 40 ++--- ios/Runner.xcodeproj/project.pbxproj | 35 ++-- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- lib/main.dart | 6 +- pubspec.lock | 170 +++++++++++++----- pubspec.yaml | 7 +- 9 files changed, 183 insertions(+), 85 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index e38ba2a..35d7d20 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -48,6 +48,7 @@ android { applicationId "net.lucien144.fyx" minSdkVersion 19 targetSdkVersion 31 + multiDexEnabled true versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/android/build.gradle b/android/build.gradle index 668f9e2..e87c088 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.10' repositories { google() jcenter() @@ -7,7 +7,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.5.4' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.31" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" classpath 'com.google.gms:google-services:4.3.10' } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 63ab3ae..a93de75 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.2-bin.zip +rl=https\://services.gradle.org/distributions/gradle-6.9.2-bin.zip diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6844218..9c515d7 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -126,7 +126,7 @@ PODS: - Flutter - package_info_plus (0.4.5): - Flutter - - path_provider (0.0.1): + - path_provider_ios (0.0.1): - Flutter - "permission_handler (5.1.0+2)": - Flutter @@ -140,15 +140,15 @@ PODS: - Sentry (~> 7.5.1) - share (0.0.1): - Flutter - - shared_preferences (0.0.1): + - shared_preferences_ios (0.0.1): - Flutter - sqflite (0.0.2): - Flutter - FMDB (>= 2.7.5) - Toast (4.0.0) - - url_launcher (0.0.1): + - url_launcher_ios (0.0.1): - Flutter - - video_player (0.0.1): + - video_player_avfoundation (0.0.1): - Flutter - wakelock (0.0.1): - Flutter @@ -166,14 +166,14 @@ DEPENDENCIES: - image_picker (from `.symlinks/plugins/image_picker/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - - path_provider (from `.symlinks/plugins/path_provider/ios`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - permission_handler (from `.symlinks/plugins/permission_handler/ios`) - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - share (from `.symlinks/plugins/share/ios`) - - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - - url_launcher (from `.symlinks/plugins/url_launcher/ios`) - - video_player (from `.symlinks/plugins/video_player/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`) - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) @@ -215,22 +215,22 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" - path_provider: - :path: ".symlinks/plugins/path_provider/ios" + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" permission_handler: :path: ".symlinks/plugins/permission_handler/ios" sentry_flutter: :path: ".symlinks/plugins/sentry_flutter/ios" share: :path: ".symlinks/plugins/share/ios" - shared_preferences: - :path: ".symlinks/plugins/shared_preferences/ios" + shared_preferences_ios: + :path: ".symlinks/plugins/shared_preferences_ios/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" - url_launcher: - :path: ".symlinks/plugins/url_launcher/ios" - video_player: - :path: ".symlinks/plugins/video_player/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + video_player_avfoundation: + :path: ".symlinks/plugins/video_player_avfoundation/ios" wakelock: :path: ".symlinks/plugins/wakelock/ios" webview_flutter: @@ -258,17 +258,17 @@ SPEC CHECKSUMS: nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e - path_provider: d1e9807085df1f9cc9318206cd649dc0b76be3de + path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 Sentry: 5c5dd4005f3b7b9765d5a8871232cddbd0d888b7 sentry_flutter: 4cd99764f9fe01c9415790d1f3fb1c7fd3a5cbe9 share: 0b2c3e82132f5888bccca3351c504d0003b3b410 - shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d + shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 - url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef - video_player: ecd305f42e9044793efd34846e1ce64c31ea6fcb + url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de + video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f webview_flutter: 3603125dfd3bcbc9d8d418c3f80aeecf331c068b diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 1403839..cfba592 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -168,7 +168,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1130; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -272,13 +272,13 @@ "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", "${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework", - "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", + "${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework", "${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework", "${BUILT_PRODUCTS_DIR}/share/share.framework", - "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences_ios/shared_preferences_ios.framework", "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", - "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", - "${BUILT_PRODUCTS_DIR}/video_player/video_player.framework", + "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", + "${BUILT_PRODUCTS_DIR}/video_player_avfoundation/video_player_avfoundation.framework", "${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework", "${BUILT_PRODUCTS_DIR}/webview_flutter/webview_flutter.framework", ); @@ -301,13 +301,13 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player_avfoundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/webview_flutter.framework", ); @@ -429,7 +429,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -565,7 +568,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -597,7 +603,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index ad6250a..c87d15a 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ =2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.10.2" diff --git a/pubspec.yaml b/pubspec.yaml index 646aad2..25ab12d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,9 +15,10 @@ version: 0.8.2+45 environment: sdk: ">=2.12.0 <3.0.0" + flutter: ^2.10.2 dependencies: - provider: ^5.0.0 + provider: ^6.0.0 meta: ^1.3.0 dio: ^4.0.0 flutter_sticky_header: ^0.6.0 @@ -51,7 +52,7 @@ dependencies: firebase_messaging: ^11.1.0 flutter_highlight: ^0.7.0 image_gallery_saver: ^1.7.1 - path_provider: 2.0.6 + path_provider: 2.0.9 permission_handler: 8.1.6 # Dart-lang core plugins 👇 @@ -70,7 +71,7 @@ dependencies: dependency_overrides: # # https://github.com/flutter/flutter/issues/44435#issuecomment-817583694 # webview_flutter: ^2.0.10 - package_info_plus: ^1.3.0 +# package_info_plus: ^1.3.0 dev_dependencies: hive_generator: ^1.1.0 From 3ed6ed515666a71593f7c11caa614cf475dfc53a Mon Sep 17 00:00:00 2001 From: David Rehak Date: Thu, 24 Feb 2022 22:04:51 +0100 Subject: [PATCH 17/38] Fix #300 - check if content_raw does not contain DiscussionWelcome --- lib/model/Post.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/model/Post.dart b/lib/model/Post.dart index a3d75e7..489db22 100644 --- a/lib/model/Post.dart +++ b/lib/model/Post.dart @@ -36,7 +36,9 @@ class Post { this._canBeDeleted = json['can_be_deleted'] ?? false; this._canBeReminded = json['can_be_reminded'] ?? false; - if (json['content_raw'] != null) { + if (json['content_raw'] != null && + json['content_raw']['data'] != null && + !json['content_raw']['data'].containsKey('DiscussionWelcome')) { switch (json['content_raw']['type']) { case 'poll': this._content = ContentPoll.fromJson(json['content_raw']['data'], discussionId: json['discussion_id'], postId: json['id']); From 11c26a5f31010960ca2f0366d56d501c37158630 Mon Sep 17 00:00:00 2001 From: Kejml Date: Wed, 23 Feb 2022 23:39:30 +0100 Subject: [PATCH 18/38] Basic dice parsing from json --- lib/model/post/content/Dice.dart | 45 +++++++++++++++++++++ lib/model/post/dice/DiceComputedValues.dart | 13 ++++++ lib/model/post/dice/DiceRoll.dart | 15 +++++++ 3 files changed, 73 insertions(+) create mode 100644 lib/model/post/content/Dice.dart create mode 100644 lib/model/post/dice/DiceComputedValues.dart create mode 100644 lib/model/post/dice/DiceRoll.dart diff --git a/lib/model/post/content/Dice.dart b/lib/model/post/content/Dice.dart new file mode 100644 index 0000000..1a7e493 --- /dev/null +++ b/lib/model/post/content/Dice.dart @@ -0,0 +1,45 @@ +import 'package:fyx/model/enums/PostTypeEnum.dart'; +import 'package:fyx/model/post/Content.dart'; +import 'package:fyx/model/post/dice/DiceComputedValues.dart'; +import 'package:fyx/model/post/dice/DiceRoll.dart'; + +class ContentDice extends Content { + String _reason = ''; + int _diceCount = 0; + int _diceSides = 0; + int _allowRollsUntil = 0; + int _showRollsAfter = 0; + List _rolls = []; + DiceComputedValues? _computedValues; + + String get reason => _reason; + + int get diceCount => _diceCount; + + int get diceSides => _diceSides; + + int get allowRollsUntil => _allowRollsUntil; + + int get showRollsAfter => _showRollsAfter; + + bool get userDidRoll => _computedValues?.userDidRoll ?? true; + + List get rolls => _rolls; + + ContentDice.fromJson(Map json) : super(PostTypeEnum.dice, isCompact: false) { + _reason = json['reason'] ?? ''; + _diceCount = json['dice_count'] ?? 0; + _diceSides = json['dice_sides'] ?? 0; + _allowRollsUntil = json['allow_rolls_until'] != null ? DateTime.parse(json['allow_rolls_until']).millisecondsSinceEpoch : 0; + _showRollsAfter = json['show_rolls_after'] != null ? DateTime.parse(json['show_rolls_after']).millisecondsSinceEpoch : 0; + if (json['rolls'] != null) { + _rolls = []; + (json['rolls'] as List).forEach((userRoll) { + _rolls.add(new DiceRoll.fromJson(userRoll as Map)); + }); + } + if (json['computed_values'] != null) { + _computedValues = DiceComputedValues.fromJson(json['computed_values']); + } + } +} diff --git a/lib/model/post/dice/DiceComputedValues.dart b/lib/model/post/dice/DiceComputedValues.dart new file mode 100644 index 0000000..12c112c --- /dev/null +++ b/lib/model/post/dice/DiceComputedValues.dart @@ -0,0 +1,13 @@ +class DiceComputedValues { + bool _canModify = false; + bool _userDidRoll = true; + + bool get canModify => _canModify; + + bool get userDidRoll => _userDidRoll; + + DiceComputedValues.fromJson(Map json) { + _canModify = json['can_modify'] ?? false; + _userDidRoll = json['user_did_roll'] ?? false; + } +} diff --git a/lib/model/post/dice/DiceRoll.dart b/lib/model/post/dice/DiceRoll.dart new file mode 100644 index 0000000..ee6bfba --- /dev/null +++ b/lib/model/post/dice/DiceRoll.dart @@ -0,0 +1,15 @@ + + +class DiceRoll { + String _user = ''; + List _rolls = []; + + String get user => _user; + + List get rolls => _rolls; + + DiceRoll.fromJson(Map json) { + _user = json['user']['username'] ?? ''; + _rolls = json['rolls'].cast(); + } +} From bd6a53de16de12cf238b45de0be08d1f3deac073 Mon Sep 17 00:00:00 2001 From: Kejml Date: Fri, 25 Feb 2022 23:33:01 +0100 Subject: [PATCH 19/38] Basic dice rendering --- lib/components/ContentBoxLayout.dart | 6 +- lib/components/post/Dice.dart | 94 ++++++++++++++++++++++++++++ lib/model/Post.dart | 4 ++ lib/model/post/content/Dice.dart | 5 +- 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 lib/components/post/Dice.dart diff --git a/lib/components/ContentBoxLayout.dart b/lib/components/ContentBoxLayout.dart index 4add767..1236c36 100644 --- a/lib/components/ContentBoxLayout.dart +++ b/lib/components/ContentBoxLayout.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_html/shims/dart_ui.dart'; import 'package:fyx/components/post/Advertisement.dart'; +import 'package:fyx/components/post/Dice.dart'; import 'package:fyx/components/post/Poll.dart'; import 'package:fyx/components/post/PostFooterLink.dart'; import 'package:fyx/components/post/PostHeroAttachment.dart'; @@ -11,11 +12,12 @@ import 'package:fyx/model/enums/PostTypeEnum.dart'; import 'package:fyx/model/post/Content.dart'; import 'package:fyx/model/post/Image.dart' as model; import 'package:fyx/model/post/content/Advertisement.dart'; +import 'package:fyx/model/post/content/Dice.dart'; import 'package:fyx/model/post/content/Poll.dart'; import 'package:fyx/theme/T.dart'; import 'package:fyx/theme/UnreadBadgeDecoration.dart'; -import 'package:fyx/theme/skin/SkinColors.dart'; import 'package:fyx/theme/skin/Skin.dart'; +import 'package:fyx/theme/skin/SkinColors.dart'; enum LAYOUT_TYPES { textOnly, oneImageOnly, attachmentsOnly, attachmentsAndText } @@ -199,6 +201,8 @@ class ContentBoxLayout extends StatelessWidget { switch (this.content.contentType) { case PostTypeEnum.poll: return Poll(content as ContentPoll); + case PostTypeEnum.dice: + return Dice(content as ContentDice); case PostTypeEnum.text: return PostHtml(content); case PostTypeEnum.advertisement: diff --git a/lib/components/post/Dice.dart b/lib/components/post/Dice.dart new file mode 100644 index 0000000..b5c884c --- /dev/null +++ b/lib/components/post/Dice.dart @@ -0,0 +1,94 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:fyx/model/post/content/Dice.dart'; +import 'package:fyx/theme/Helpers.dart'; +import 'package:fyx/theme/skin/Skin.dart'; +import 'package:fyx/theme/skin/SkinColors.dart'; + +import '../Avatar.dart'; + +class Dice extends StatefulWidget { + + final ContentDice content; + + Dice(this.content); + + @override + _DiceState createState() => _DiceState(); + + + +} +class _DiceState extends State { + bool _loading = false; + ContentDice? _dice; + ScrollController controller = ScrollController(); + + + @override + void initState() { + _dice = widget.content; + super.initState(); + } + + Widget buildRolls(BuildContext context) { + SkinColors colors = Skin.of(context).theme.colors; + + return ListView.builder( + physics: NeverScrollableScrollPhysics(), + controller: controller, + itemBuilder: (context, index) { + final roll = _dice!.rolls[index]; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: colors.pollAnswer, + ), + child: + Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Padding( + padding: const EdgeInsets.only(top: 8), + child: Text( + roll.rolls.reduce((a, b) => a + b).toString(), + style: TextStyle(fontSize: 14), + ), + ), + Tooltip( + message: '${roll.user}: ${roll.rolls.join(", ")}', + waitDuration: Duration(milliseconds: 0), + showDuration: + Duration(milliseconds: 1500 + (roll.rolls.length * 300)), + child: Padding( + padding: const EdgeInsets.only(left: 5, bottom: 0), + child: Avatar(Helpers.avatarUrl(roll.user), + size: 22, isHighlighted: false), + ), + ) + ]), + ), + ); + }, + itemCount: _dice!.rolls.length, + shrinkWrap: true, + padding: const EdgeInsets.all(0)); + } + + @override + Widget build(BuildContext context) { + SkinColors colors = Skin.of(context).theme.colors; + + return Container( + alignment: Alignment.centerLeft, + child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + Text(_dice!.reason, style: DefaultTextStyle.of(context).style.copyWith(fontSize: 20, fontWeight: FontWeight.bold)), + SizedBox(height: 8,), + buildRolls(context), + ]), + color: colors.pollBackground, + padding: EdgeInsets.all(15) + ); + } +} diff --git a/lib/model/Post.dart b/lib/model/Post.dart index a3d75e7..9fd1f15 100644 --- a/lib/model/Post.dart +++ b/lib/model/Post.dart @@ -1,6 +1,7 @@ // ignore_for_file: non_constant_identifier_names import 'package:fyx/model/post/Content.dart'; import 'package:fyx/model/post/content/Advertisement.dart'; +import 'package:fyx/model/post/content/Dice.dart'; import 'package:fyx/model/post/content/Poll.dart'; import 'package:fyx/model/post/content/Regular.dart'; import 'package:fyx/theme/Helpers.dart'; @@ -41,6 +42,9 @@ class Post { case 'poll': this._content = ContentPoll.fromJson(json['content_raw']['data'], discussionId: json['discussion_id'], postId: json['id']); break; + case 'dice': + this._content = ContentDice.fromJson(json['content_raw']['data'], discussionId: json['discussion_id'], postId: json['id']); + break; case 'advertisement': this._canReply = false; this._content = ContentAdvertisement.fromPostJson(json); diff --git a/lib/model/post/content/Dice.dart b/lib/model/post/content/Dice.dart index 1a7e493..ba37709 100644 --- a/lib/model/post/content/Dice.dart +++ b/lib/model/post/content/Dice.dart @@ -4,6 +4,9 @@ import 'package:fyx/model/post/dice/DiceComputedValues.dart'; import 'package:fyx/model/post/dice/DiceRoll.dart'; class ContentDice extends Content { + int postId = 0; + int discussionId = 0; + String _reason = ''; int _diceCount = 0; int _diceSides = 0; @@ -26,7 +29,7 @@ class ContentDice extends Content { List get rolls => _rolls; - ContentDice.fromJson(Map json) : super(PostTypeEnum.dice, isCompact: false) { + ContentDice.fromJson(Map json, {this.postId = 0, this.discussionId = 0}) : super(PostTypeEnum.dice, isCompact: false) { _reason = json['reason'] ?? ''; _diceCount = json['dice_count'] ?? 0; _diceSides = json['dice_sides'] ?? 0; From 0281ee5040f125bb75e6c86c85037c2afb128c04 Mon Sep 17 00:00:00 2001 From: Kejml Date: Fri, 25 Feb 2022 23:44:38 +0100 Subject: [PATCH 20/38] Allow dice rolls, fixes #301 --- lib/components/post/Dice.dart | 23 +++++++++++++++++++++++ lib/controllers/ApiController.dart | 7 +++++++ lib/controllers/ApiProvider.dart | 4 ++++ lib/controllers/IApiProvider.dart | 1 + test/api_test.dart | 6 ++++++ 5 files changed, 41 insertions(+) diff --git a/lib/components/post/Dice.dart b/lib/components/post/Dice.dart index b5c884c..50ae8c4 100644 --- a/lib/components/post/Dice.dart +++ b/lib/components/post/Dice.dart @@ -1,8 +1,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:fyx/controllers/ApiController.dart'; import 'package:fyx/model/post/content/Dice.dart'; import 'package:fyx/theme/Helpers.dart'; +import 'package:fyx/theme/T.dart'; import 'package:fyx/theme/skin/Skin.dart'; import 'package:fyx/theme/skin/SkinColors.dart'; @@ -86,6 +88,27 @@ class _DiceState extends State { Text(_dice!.reason, style: DefaultTextStyle.of(context).style.copyWith(fontSize: 20, fontWeight: FontWeight.bold)), SizedBox(height: 8,), buildRolls(context), + if (!_dice!.userDidRoll) + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: CupertinoButton( + onPressed: _loading ? null : () async { + setState(() => _loading = true); + try { + var poll = await ApiController().rollDice(_dice!.discussionId, _dice!.postId); + setState(() => _dice = poll); + } catch (error) { + T.error(error.toString(), bg: colors.danger); + } finally { + setState(() => _loading = false); + } + }, + child: _loading ? CupertinoActivityIndicator() : Text('Hodit! ${_dice!.diceCount}d${_dice!.diceSides}'), + color: colors.primary, + padding: EdgeInsets.all(0), + disabledColor: colors.disabled, + ), + ) ]), color: colors.pollBackground, padding: EdgeInsets.all(15) diff --git a/lib/controllers/ApiController.dart b/lib/controllers/ApiController.dart index 780d943..2c00b09 100644 --- a/lib/controllers/ApiController.dart +++ b/lib/controllers/ApiController.dart @@ -9,6 +9,7 @@ import 'package:fyx/exceptions/AuthException.dart'; import 'package:fyx/model/Credentials.dart'; import 'package:fyx/model/Post.dart'; import 'package:fyx/model/ResponseContext.dart'; +import 'package:fyx/model/post/content/Dice.dart'; import 'package:fyx/model/post/content/Poll.dart'; import 'package:fyx/model/provider/NotificationsModel.dart'; import 'package:fyx/model/reponses/BookmarksAllResponse.dart'; @@ -306,6 +307,12 @@ class ApiController { return ContentPoll.fromJson(json['content_raw']['data'], discussionId: json['discussion_id'] ?? 0, postId: json['post_id'] ?? 0); } + Future rollDice(discussionId, postId) async { + Response response = await provider.rollDice(discussionId, postId); + Map json = response.data; + return ContentDice.fromJson(json['content_raw']['data'], discussionId: json['discussion_id'] ?? 0, postId: json['post_id'] ?? 0); + } + throwAuthException(LoginResponse loginResponse, {String message: ''}) { if (loginResponse.error) { throw AuthException(loginResponse.message); diff --git a/lib/controllers/ApiProvider.dart b/lib/controllers/ApiProvider.dart index 1d3d5f7..c418158 100644 --- a/lib/controllers/ApiProvider.dart +++ b/lib/controllers/ApiProvider.dart @@ -195,4 +195,8 @@ class ApiProvider implements IApiProvider { Future votePoll(int discussionId, int postId, List votes) async { return await dio.post('$URL/discussion/$discussionId/poll/$postId/vote/${votes.join(',')}'); } + + Future rollDice(int discussionId, int postId) async { + return await dio.post('$URL/discussion/$discussionId/dice/$postId/roll'); + } } diff --git a/lib/controllers/IApiProvider.dart b/lib/controllers/IApiProvider.dart index c4af986..1c348b4 100644 --- a/lib/controllers/IApiProvider.dart +++ b/lib/controllers/IApiProvider.dart @@ -34,4 +34,5 @@ abstract class IApiProvider { Future giveRating(int discussionId, int postId, bool add, bool confirm, bool remove); Future getPostRatings(int discussionId, int postId); Future votePoll(int discussionId, int postId, List votes); + Future rollDice(int discussionId, int postId); } diff --git a/test/api_test.dart b/test/api_test.dart index 1228373..4fe2bf3 100644 --- a/test/api_test.dart +++ b/test/api_test.dart @@ -170,6 +170,12 @@ class ApiMock implements IApiProvider { throw UnimplementedError(); } + @override + Future rollDice(int discussionId, int postId) { + // TODO: implement rollDice + throw UnimplementedError(); + } + @override Future deleteDiscussionMessage(int discussionId, int postId) { // TODO: implement deleteDiscussionMessage From cb9bc30ac466ee61a72e7f685ea0f931aa287ee4 Mon Sep 17 00:00:00 2001 From: Kejml Date: Fri, 25 Feb 2022 23:49:30 +0100 Subject: [PATCH 21/38] Correctly decide whether use can roll or not --- lib/components/post/Dice.dart | 2 +- lib/model/post/content/Dice.dart | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/components/post/Dice.dart b/lib/components/post/Dice.dart index 50ae8c4..587f8af 100644 --- a/lib/components/post/Dice.dart +++ b/lib/components/post/Dice.dart @@ -88,7 +88,7 @@ class _DiceState extends State { Text(_dice!.reason, style: DefaultTextStyle.of(context).style.copyWith(fontSize: 20, fontWeight: FontWeight.bold)), SizedBox(height: 8,), buildRolls(context), - if (!_dice!.userDidRoll) + if (_dice!.canRoll) Padding( padding: const EdgeInsets.only(top: 16.0), child: CupertinoButton( diff --git a/lib/model/post/content/Dice.dart b/lib/model/post/content/Dice.dart index ba37709..859f71c 100644 --- a/lib/model/post/content/Dice.dart +++ b/lib/model/post/content/Dice.dart @@ -25,10 +25,16 @@ class ContentDice extends Content { int get showRollsAfter => _showRollsAfter; - bool get userDidRoll => _computedValues?.userDidRoll ?? true; - List get rolls => _rolls; + bool get canRoll { + bool _canRoll = !(_computedValues?.userDidRoll ?? false); + if (_allowRollsUntil > 0) { + _canRoll = _canRoll && _allowRollsUntil > DateTime.now().millisecondsSinceEpoch; + } + return _canRoll; + } + ContentDice.fromJson(Map json, {this.postId = 0, this.discussionId = 0}) : super(PostTypeEnum.dice, isCompact: false) { _reason = json['reason'] ?? ''; _diceCount = json['dice_count'] ?? 0; From f6c8baf88b99fa2bbea8f60940197b6a6b65d020 Mon Sep 17 00:00:00 2001 From: Kejml Date: Sat, 26 Feb 2022 00:05:16 +0100 Subject: [PATCH 22/38] Show dates regarding dice rolls --- lib/components/post/Dice.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/components/post/Dice.dart b/lib/components/post/Dice.dart index 587f8af..9abdb6b 100644 --- a/lib/components/post/Dice.dart +++ b/lib/components/post/Dice.dart @@ -86,6 +86,17 @@ class _DiceState extends State { alignment: Alignment.centerLeft, child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text(_dice!.reason, style: DefaultTextStyle.of(context).style.copyWith(fontSize: 20, fontWeight: FontWeight.bold)), + if (_dice!.showRollsAfter > 0 && _dice!.showRollsAfter > DateTime.now().millisecondsSinceEpoch) + Padding( + padding: EdgeInsets.only(top: 8), + child: Text('Výsledky se zobrazí po ${Helpers.absoluteTime(_dice!.showRollsAfter)}', style: TextStyle(fontStyle: FontStyle.italic),) + ), + + if (_dice!.allowRollsUntil > 0 && _dice!.allowRollsUntil > DateTime.now().millisecondsSinceEpoch) + Padding( + padding: EdgeInsets.only(top: 8), + child: Text('Házet možné do ${Helpers.absoluteTime(_dice!.allowRollsUntil)}', style: TextStyle(fontStyle: FontStyle.italic),) + ), SizedBox(height: 8,), buildRolls(context), if (_dice!.canRoll) From a786212b4456c395c3429714e9a95c2472f441cb Mon Sep 17 00:00:00 2001 From: David Rehak Date: Sat, 26 Feb 2022 20:52:45 +0100 Subject: [PATCH 23/38] Depedencies upgrade --- pubspec.yaml | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 25ab12d..70e96cb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,46 +18,46 @@ environment: flutter: ^2.10.2 dependencies: - provider: ^6.0.0 - meta: ^1.3.0 - dio: ^4.0.0 + provider: ^6.0.2 + meta: ^1.7.0 + dio: ^4.0.3 flutter_sticky_header: ^0.6.0 html: ^0.15.0 - flutter_html: ^2.1.0 - shared_preferences: ^2.0.6 - fluttertoast: ^8.0.8 + flutter_html: ^2.2.1 + shared_preferences: ^2.0.13 + fluttertoast: ^8.0.9 html_unescape: ^2.0.0 - cached_network_image: ^3.0.0 + cached_network_image: ^3.2.0 carousel_slider: ^4.0.0 - device_info: ^2.0.2 + device_info: ^2.0.3 package_info: ^2.0.2 - url_launcher: ^6.0.9 + url_launcher: ^6.0.20 photo_view: ^0.13.0 auto_size_text: ^3.0.0-nullsafety.0 - image_picker: ^0.8.4+4 + image_picker: ^0.8.4+10 path: ^1.8.0 - image: ^3.0.2 - async: ^2.6.1 - chewie: ^1.2.2 - video_player: ^2.2.7 + image: ^3.1.3 + async: ^2.8.2 + chewie: ^1.3.0 + video_player: ^2.2.19 flutter_cupertino_settings: ^0.5.0 - flutter_markdown: ^0.6.2 - http: ^0.13.3 + flutter_markdown: ^0.6.9 + http: ^0.13.4 share: ^2.0.4 - hive: ^2.0.4 + hive: ^2.0.6 hive_flutter: ^1.1.0 - firebase_analytics: ^8.1.2 - sentry_flutter: 6.1.0 - flutter_dotenv: ^5.0.0 + firebase_analytics: ^8.3.4 + sentry_flutter: 6.3.0 + flutter_dotenv: ^5.0.2 firebase_messaging: ^11.1.0 flutter_highlight: ^0.7.0 image_gallery_saver: ^1.7.1 path_provider: 2.0.9 - permission_handler: 8.1.6 + permission_handler: 9.2.0 # Dart-lang core plugins 👇 http_parser: ^4.0.0 - mime: ^1.0.0 + mime: ^1.0.1 intl: ^0.17.0 flutter: From e9dddf0cb81f9a38ca4d5f01cedd82af750f190c Mon Sep 17 00:00:00 2001 From: David Rehak Date: Sun, 27 Feb 2022 09:08:27 +0100 Subject: [PATCH 24/38] Update of Flutter to 2.10.2 in .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bab9cef..b9665b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ addons: packages: - lib32stdc++6 install: - - git clone --depth 1 --branch 2.5.3 https://github.com/flutter/flutter.git + - git clone --depth 1 --branch 2.10.2 https://github.com/flutter/flutter.git - ./flutter/bin/flutter doctor - gem install coveralls-lcov - cp .env.example .env From a57e5fedb386f8d913e744bbe57dc6ed762062a9 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Wed, 9 Mar 2022 21:29:46 +0100 Subject: [PATCH 25/38] fix: another locales added, default EN #205 --- lib/SkinnedApp.dart | 49 +++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/SkinnedApp.dart b/lib/SkinnedApp.dart index 19fbd3a..8f73260 100644 --- a/lib/SkinnedApp.dart +++ b/lib/SkinnedApp.dart @@ -16,29 +16,30 @@ class SkinnedApp extends StatelessWidget { @override Widget build(BuildContext context) { return CupertinoApp( - title: 'Fyx', - theme: Skin.of(context).theme.data, - home: MainRepository().credentials != null && MainRepository().credentials!.isValid ? HomePage() : LoginPage(), - debugShowCheckedModeBanner: FyxApp.isDev, - onUnknownRoute: (RouteSettings settings) => CupertinoPageRoute(builder: (_) => DiscussionPage(), settings: settings), - onGenerateRoute: FyxApp.routes, - navigatorKey: FyxApp.navigatorKey, - navigatorObservers: [ - FyxApp.routeObserver, - FirebaseAnalyticsObserver( - analytics: FyxApp.analytics, - onError: (error) async => await Sentry.captureException( - error, - )) - ], - localizationsDelegates: [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [ - Locale('cs', ''), - ] - ); + title: 'Fyx', + theme: Skin.of(context).theme.data, + home: MainRepository().credentials != null && MainRepository().credentials!.isValid ? HomePage() : LoginPage(), + debugShowCheckedModeBanner: FyxApp.isDev, + onUnknownRoute: (RouteSettings settings) => CupertinoPageRoute(builder: (_) => DiscussionPage(), settings: settings), + onGenerateRoute: FyxApp.routes, + navigatorKey: FyxApp.navigatorKey, + navigatorObservers: [ + FyxApp.routeObserver, + FirebaseAnalyticsObserver( + analytics: FyxApp.analytics, + onError: (error) async => await Sentry.captureException( + error, + )) + ], + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [ + Locale('en', ''), + Locale('cs', ''), + Locale('sk', ''), + ]); } } From 3d84ec7bcaf47688a223b832ba219c17ba5d76fb Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Wed, 9 Mar 2022 21:42:22 +0100 Subject: [PATCH 26/38] style: #302 --- lib/components/post/Dice.dart | 64 +++++++++++++++++------------------ 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/lib/components/post/Dice.dart b/lib/components/post/Dice.dart index 9abdb6b..0860ac8 100644 --- a/lib/components/post/Dice.dart +++ b/lib/components/post/Dice.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:fyx/components/Avatar.dart'; import 'package:fyx/controllers/ApiController.dart'; import 'package:fyx/model/post/content/Dice.dart'; import 'package:fyx/theme/Helpers.dart'; @@ -8,26 +9,20 @@ import 'package:fyx/theme/T.dart'; import 'package:fyx/theme/skin/Skin.dart'; import 'package:fyx/theme/skin/SkinColors.dart'; -import '../Avatar.dart'; - class Dice extends StatefulWidget { - final ContentDice content; Dice(this.content); @override _DiceState createState() => _DiceState(); - - - } + class _DiceState extends State { bool _loading = false; ContentDice? _dice; ScrollController controller = ScrollController(); - @override void initState() { _dice = widget.content; @@ -49,8 +44,7 @@ class _DiceState extends State { decoration: BoxDecoration( color: colors.pollAnswer, ), - child: - Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(top: 8), child: Text( @@ -61,12 +55,10 @@ class _DiceState extends State { Tooltip( message: '${roll.user}: ${roll.rolls.join(", ")}', waitDuration: Duration(milliseconds: 0), - showDuration: - Duration(milliseconds: 1500 + (roll.rolls.length * 300)), + showDuration: Duration(milliseconds: 1500 + (roll.rolls.length * 300)), child: Padding( padding: const EdgeInsets.only(left: 5, bottom: 0), - child: Avatar(Helpers.avatarUrl(roll.user), - size: 22, isHighlighted: false), + child: Avatar(Helpers.avatarUrl(roll.user), size: 22, isHighlighted: false), ), ) ]), @@ -89,31 +81,38 @@ class _DiceState extends State { if (_dice!.showRollsAfter > 0 && _dice!.showRollsAfter > DateTime.now().millisecondsSinceEpoch) Padding( padding: EdgeInsets.only(top: 8), - child: Text('Výsledky se zobrazí po ${Helpers.absoluteTime(_dice!.showRollsAfter)}', style: TextStyle(fontStyle: FontStyle.italic),) - ), - + child: Text( + 'Výsledky se zobrazí po ${Helpers.absoluteTime(_dice!.showRollsAfter)}', + style: TextStyle(fontStyle: FontStyle.italic), + )), if (_dice!.allowRollsUntil > 0 && _dice!.allowRollsUntil > DateTime.now().millisecondsSinceEpoch) Padding( padding: EdgeInsets.only(top: 8), - child: Text('Házet možné do ${Helpers.absoluteTime(_dice!.allowRollsUntil)}', style: TextStyle(fontStyle: FontStyle.italic),) - ), - SizedBox(height: 8,), - buildRolls(context), + child: Text( + 'Házet možné do ${Helpers.absoluteTime(_dice!.allowRollsUntil)}', + style: TextStyle(fontStyle: FontStyle.italic), + )), + SizedBox( + height: 8, + ), + buildRolls(context), if (_dice!.canRoll) Padding( padding: const EdgeInsets.only(top: 16.0), child: CupertinoButton( - onPressed: _loading ? null : () async { - setState(() => _loading = true); - try { - var poll = await ApiController().rollDice(_dice!.discussionId, _dice!.postId); - setState(() => _dice = poll); - } catch (error) { - T.error(error.toString(), bg: colors.danger); - } finally { - setState(() => _loading = false); - } - }, + onPressed: _loading + ? null + : () async { + setState(() => _loading = true); + try { + var poll = await ApiController().rollDice(_dice!.discussionId, _dice!.postId); + setState(() => _dice = poll); + } catch (error) { + T.error(error.toString(), bg: colors.danger); + } finally { + setState(() => _loading = false); + } + }, child: _loading ? CupertinoActivityIndicator() : Text('Hodit! ${_dice!.diceCount}d${_dice!.diceSides}'), color: colors.primary, padding: EdgeInsets.all(0), @@ -122,7 +121,6 @@ class _DiceState extends State { ) ]), color: colors.pollBackground, - padding: EdgeInsets.all(15) - ); + padding: EdgeInsets.all(15)); } } From 14f5762bbf7ce511e4f7979df5a37924ea35bc31 Mon Sep 17 00:00:00 2001 From: David Rehak Date: Sun, 13 Mar 2022 14:28:38 +0100 Subject: [PATCH 27/38] Comment out Multidex --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 35d7d20..be5b94e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -48,7 +48,7 @@ android { applicationId "net.lucien144.fyx" minSdkVersion 19 targetSdkVersion 31 - multiDexEnabled true + //multiDexEnabled true // Uncomment for dev versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" From b51fe7869bfe8fa3fe39343585751a43e6129b4b Mon Sep 17 00:00:00 2001 From: lucien144 Date: Tue, 15 Mar 2022 10:14:45 +0100 Subject: [PATCH 28/38] style: revert, keep the styling as is --- lib/main.dart | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 lib/main.dart diff --git a/lib/main.dart b/lib/main.dart deleted file mode 100644 index 9c7e46e..0000000 --- a/lib/main.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:fyx/FyxApp.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - ByteData data = - await PlatformAssetBundle().load('assets/lets-encrypt-r3.cer'); - SecurityContext.defaultContext - .setTrustedCertificatesBytes(data.buffer.asUint8List()); - await dotenv.load(); - - runZonedGuarded( - () async { - await FyxApp.init(); - SentryFlutter.init((options) { - options.dsn = dotenv.env['SENTRY_KEY']; - options.environment = 'development'; - }, appRunner: () => runApp(FyxApp()..setEnv(Environment.dev))); - }, - (error, stackTrace) async { - try { - await Sentry.captureException( - error, - stackTrace: stackTrace, - ); - print('Error sent to sentry.io: $error'); - } catch (e) { - print('Sending report to sentry.io failed: $e'); - print('Original error: $error'); - } - }, - ); -} From 9628c0e695c6416b43170a02b40bcb96c1cbe54a Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Tue, 15 Mar 2022 21:33:42 +0100 Subject: [PATCH 29/38] Revert "style: revert, keep the styling as is" This reverts commit b51fe7869bfe8fa3fe39343585751a43e6129b4b. --- lib/main.dart | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 lib/main.dart diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..9c7e46e --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,39 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:fyx/FyxApp.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + ByteData data = + await PlatformAssetBundle().load('assets/lets-encrypt-r3.cer'); + SecurityContext.defaultContext + .setTrustedCertificatesBytes(data.buffer.asUint8List()); + await dotenv.load(); + + runZonedGuarded( + () async { + await FyxApp.init(); + SentryFlutter.init((options) { + options.dsn = dotenv.env['SENTRY_KEY']; + options.environment = 'development'; + }, appRunner: () => runApp(FyxApp()..setEnv(Environment.dev))); + }, + (error, stackTrace) async { + try { + await Sentry.captureException( + error, + stackTrace: stackTrace, + ); + print('Error sent to sentry.io: $error'); + } catch (e) { + print('Sending report to sentry.io failed: $e'); + print('Original error: $error'); + } + }, + ); +} From a65cfecb4b7d67a622940dfcfe239a6b5adbbae0 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Tue, 22 Mar 2022 17:55:57 +0100 Subject: [PATCH 30/38] chore: ios build improvement --- build.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 005a903..6e5026a 100755 --- a/build.sh +++ b/build.sh @@ -56,6 +56,8 @@ if [ $ios == true ]; then # shellcheck disable=SC2059 printf "$GREEN Building iOS: ${version}$NC\n" flutter clean + flutter pub get + (cd ios && pod cache clean --all && pod update) flutter build ios -t lib/main_production.dart open ios/Runner.xcworkspace /usr/bin/osascript -e "display notification \"iOS built.\"" @@ -72,4 +74,4 @@ if [ $android == true ]; then flutter build appbundle -t lib/main_production.dart open build/app/outputs/bundle/release/ /usr/bin/osascript -e "display notification \"Android built.\"" -fi \ No newline at end of file +fi From 775bb5f8aebcd117e18887e2749f541838fd22d4 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Tue, 29 Mar 2022 17:46:15 +0200 Subject: [PATCH 31/38] feat: removing colored rating, readability --- lib/components/post/PostListItem.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/components/post/PostListItem.dart b/lib/components/post/PostListItem.dart index ecd2efd..13d7295 100644 --- a/lib/components/post/PostListItem.dart +++ b/lib/components/post/PostListItem.dart @@ -9,7 +9,6 @@ import 'package:fyx/components/actionSheets/PostAvatarActionSheet.dart'; import 'package:fyx/components/post/PostAvatar.dart'; import 'package:fyx/components/post/PostRating.dart'; import 'package:fyx/components/post/PostThumbs.dart'; -import 'package:fyx/components/post/RatingValue.dart'; import 'package:fyx/controllers/AnalyticsProvider.dart'; import 'package:fyx/controllers/ApiController.dart'; import 'package:fyx/controllers/IApiProvider.dart'; @@ -114,10 +113,9 @@ class _PostListItemState extends State { descriptionWidget: Row( children: [ if (_post!.rating != null) - RatingValue( - _post!.rating!, - fontSize: 10, - ), + Text(Post.formatRating(_post!.rating!), + style: + TextStyle(fontSize: 10, color: _post!.rating! > 0 ? colors.success : (_post!.rating! < 0 ? colors.danger : colors.text))), if (_post!.rating != null) SizedBox(width: 8), Text( Helpers.absoluteTime(_post!.time), From e5d21d7cbf5e17568d334dd53a545f5a9b3335c7 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Tue, 29 Mar 2022 17:46:52 +0200 Subject: [PATCH 32/38] fix: #311 --- lib/pages/DiscussionPage.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/DiscussionPage.dart b/lib/pages/DiscussionPage.dart index 9ff0194..fde36f9 100644 --- a/lib/pages/DiscussionPage.dart +++ b/lib/pages/DiscussionPage.dart @@ -42,7 +42,8 @@ class _DiscussionPageState extends State { Future _fetchData(discussionId, postId, user, {String? search}) { return this._memoizer.runOnce(() { - return ApiController().loadDiscussion(discussionId, lastId: postId, user: user, search: search); + return Future.delayed( + Duration(milliseconds: 300), () => ApiController().loadDiscussion(discussionId, lastId: postId, user: user, search: search)); }); } From 3b6c2dd4075ad247246deb44846283890022167c Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Tue, 29 Mar 2022 17:48:42 +0200 Subject: [PATCH 33/38] refactor: DiscussionCommonBookmark --- lib/model/Discussion.dart | 12 ++++++++++- lib/model/DiscussionCommonBookmark.dart | 27 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 lib/model/DiscussionCommonBookmark.dart diff --git a/lib/model/Discussion.dart b/lib/model/Discussion.dart index 4fb5235..a863002 100644 --- a/lib/model/Discussion.dart +++ b/lib/model/Discussion.dart @@ -1,6 +1,7 @@ // ignore_for_file: non_constant_identifier_names import 'package:fyx/model/AccessRights.dart'; +import 'package:fyx/model/DiscussionCommonBookmark.dart'; import 'package:fyx/model/DiscussionOwner.dart'; import 'package:fyx/model/DiscussionRights.dart'; import 'package:fyx/model/enums/DiscussionTypeEnum.dart'; @@ -24,6 +25,7 @@ class Discussion { late AccessRights _access_rights; DiscussionOwner? _owner; ContentAdvertisement? _advertisement; + DiscussionCommonBookmark? _bookmark; Discussion.fromJson(Map? json) { if (json == null) { @@ -63,13 +65,19 @@ class Discussion { this._owner = DiscussionOwner.fromJson(json['owner']); } + if (json['bookmark'] is Map) { + this._bookmark = DiscussionCommonBookmark.fromJson(json['bookmark']); + } + try { this._last_visit = DateTime.parse(json['bookmark']['last_visited_at']).millisecondsSinceEpoch; } catch (error) { this._last_visit = 0; } - if (type == DiscussionTypeEnum.advertisement && json['advertisement_specific_data'] != null && json['advertisement_specific_data']['advertisement'] != null) { + if (type == DiscussionTypeEnum.advertisement && + json['advertisement_specific_data'] != null && + json['advertisement_specific_data']['advertisement'] != null) { _advertisement = ContentAdvertisement.fromDiscussionJson(json['advertisement_specific_data']); } } @@ -96,6 +104,8 @@ class Discussion { ContentAdvertisement? get advertisement => _advertisement; + DiscussionCommonBookmark? get bookmark => _bookmark; + DiscussionOwner? get owner => _owner; AccessRights get accessRights => _access_rights; diff --git a/lib/model/DiscussionCommonBookmark.dart b/lib/model/DiscussionCommonBookmark.dart new file mode 100644 index 0000000..6e43dd4 --- /dev/null +++ b/lib/model/DiscussionCommonBookmark.dart @@ -0,0 +1,27 @@ +class DiscussionCommonBookmark { + late int discussionId; + late bool bookmark; + int? categoryId; + int repliesCount = 0; + int lastSeenPostId = 0; + int lastSeenPostsCount = 0; + int lastSeenImagePostsCount = 0; + int lastSeenLinkPostsCount = 0; + int lastVisitedAt = 0; + + DiscussionCommonBookmark.fromJson(Map json) { + discussionId = json['discussion_id']; + bookmark = json['bookmark']; + categoryId = json['category_id']; + repliesCount = json['replies_count'] ?? 0; + lastSeenPostId = json['last_seen_post_id'] ?? 0; + lastSeenPostsCount = json['last_seen_posts_count'] ?? 0; + lastSeenImagePostsCount = json['last_seen_image_posts_count'] ?? 0; + lastSeenLinkPostsCount = json['last_seen_link_posts_count'] ?? 0; + try { + lastVisitedAt = DateTime.parse(json['last_visited_at']).millisecondsSinceEpoch; + } catch (error) { + lastVisitedAt = 0; + } + } +} From 75819604edb12e42ac7d2c984c06beb4c730372d Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Tue, 29 Mar 2022 17:49:10 +0200 Subject: [PATCH 34/38] chore: podfile --- ios/Podfile.lock | 60 ++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 9c515d7..7fdb024 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -44,15 +44,15 @@ PODS: - FirebaseCoreDiagnostics (~> 8.0) - GoogleUtilities/Environment (~> 7.6) - GoogleUtilities/Logger (~> 7.6) - - FirebaseCoreDiagnostics (8.10.0): + - FirebaseCoreDiagnostics (8.14.0): - GoogleDataTransport (~> 9.1) - - GoogleUtilities/Environment (~> 7.6) - - GoogleUtilities/Logger (~> 7.6) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Logger (~> 7.7) - nanopb (~> 2.30908.0) - - FirebaseInstallations (8.10.0): + - FirebaseInstallations (8.14.0): - FirebaseCore (~> 8.0) - - GoogleUtilities/Environment (~> 7.6) - - GoogleUtilities/UserDefaults (~> 7.6) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/UserDefaults (~> 7.7) - PromisesObjC (< 3.0, >= 1.2) - FirebaseMessaging (8.9.0): - FirebaseCore (~> 8.0) @@ -94,24 +94,24 @@ PODS: - GoogleUtilities/Environment (~> 7.2) - nanopb (~> 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/AppDelegateSwizzler (7.6.0): + - GoogleUtilities/AppDelegateSwizzler (7.7.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (7.6.0): + - GoogleUtilities/Environment (7.7.0): - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.6.0): + - GoogleUtilities/Logger (7.7.0): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (7.6.0): + - GoogleUtilities/MethodSwizzler (7.7.0): - GoogleUtilities/Logger - - GoogleUtilities/Network (7.6.0): + - GoogleUtilities/Network (7.7.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.6.0)" - - GoogleUtilities/Reachability (7.6.0): + - "GoogleUtilities/NSData+zlib (7.7.0)" + - GoogleUtilities/Reachability (7.7.0): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.6.0): + - GoogleUtilities/UserDefaults (7.7.0): - GoogleUtilities/Logger - image_gallery_saver (1.5.0): - Flutter @@ -128,16 +128,16 @@ PODS: - Flutter - path_provider_ios (0.0.1): - Flutter - - "permission_handler (5.1.0+2)": + - permission_handler_apple (9.0.2): - Flutter - PromisesObjC (2.0.0) - - Sentry (7.5.4): - - Sentry/Core (= 7.5.4) - - Sentry/Core (7.5.4) + - Sentry (7.9.0): + - Sentry/Core (= 7.9.0) + - Sentry/Core (7.9.0) - sentry_flutter (0.0.1): - Flutter - FlutterMacOS - - Sentry (~> 7.5.1) + - Sentry (~> 7.9.0) - share (0.0.1): - Flutter - shared_preferences_ios (0.0.1): @@ -167,7 +167,7 @@ DEPENDENCIES: - package_info (from `.symlinks/plugins/package_info/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - - permission_handler (from `.symlinks/plugins/permission_handler/ios`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - share (from `.symlinks/plugins/share/ios`) - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) @@ -217,8 +217,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_ios: :path: ".symlinks/plugins/path_provider_ios/ios" - permission_handler: - :path: ".symlinks/plugins/permission_handler/ios" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" sentry_flutter: :path: ".symlinks/plugins/sentry_flutter/ios" share: @@ -244,25 +244,25 @@ SPEC CHECKSUMS: firebase_messaging: dff5cd08781ee1de988565a83c977e435405cd7e FirebaseAnalytics: 4ab446ce08a3fe52e8a4303dd997cf26276bf968 FirebaseCore: 599ee609343eaf4941bd188f85e3aa077ffe325b - FirebaseCoreDiagnostics: 56fb7216d87e0e6ec2feddefa9d8a392fe8b2c18 - FirebaseInstallations: 830327b45345ffc859eaa9c17bcd5ae893fd5425 + FirebaseCoreDiagnostics: fd0c8490f34287229c1d6c103d3a55f81ec85712 + FirebaseInstallations: 7d1d967a307c12f1aadd76844fc321cef699b1ce FirebaseMessaging: 82c4a48638f53f7b184f3cc9f6cd2cbe533ab316 Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - fluttertoast: 6122fa75143e992b1d3470f61000f591a798cc58 + fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a GoogleAppMeasurement: 837649ad3987936c232f6717c5680216f6243d24 GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940 - GoogleUtilities: 684ee790a24f73ebb2d1d966e9711c203f2a4237 + GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2 - image_picker: 9aa50e1d8cdacdbed739e925b7eea16d014367e6 + image_picker: 541dcbb3b9cf32d87eacbd957845d8651d6c62c3 nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 - permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 + permission_handler_apple: d21b38e1a4b2e041c63af9568f9165e114e507a6 PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 - Sentry: 5c5dd4005f3b7b9765d5a8871232cddbd0d888b7 - sentry_flutter: 4cd99764f9fe01c9415790d1f3fb1c7fd3a5cbe9 + Sentry: 2f7e91f247cfb05b05bd01e0b5d0692557a7687b + sentry_flutter: 7c3cb050dc23563a4ea5db438c83afdb460a2ae6 share: 0b2c3e82132f5888bccca3351c504d0003b3b410 shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 From 945bbd260cb0f8293a3620b6fafc7dde04fd7c57 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Sat, 9 Apr 2022 09:22:03 +0200 Subject: [PATCH 35/38] fix: #312 --- lib/pages/DiscussionPage.dart | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/pages/DiscussionPage.dart b/lib/pages/DiscussionPage.dart index fde36f9..bd068d8 100644 --- a/lib/pages/DiscussionPage.dart +++ b/lib/pages/DiscussionPage.dart @@ -102,11 +102,15 @@ class _DiscussionPageState extends State { alignment: Alignment.center, width: MediaQuery.of(context).size.width - 120, child: Tooltip( - message: title, - child: Text(title.replaceAll('', '\u{200B}'), style: TextStyle(color: colors.text), overflow: TextOverflow.ellipsis), - padding: EdgeInsets.all(8.0), // needed until https://github.com/flutter/flutter/issues/86170 is fixed - margin: EdgeInsets.all(8.0), - showDuration: Duration(seconds: 3), + message: title, + child: Text( + // https://github.com/flutter/flutter/issues/18761 + Characters(title).replaceAll(Characters(''), Characters('\u{200B}')).toString(), + style: TextStyle(color: colors.text), + overflow: TextOverflow.ellipsis), + padding: EdgeInsets.all(8.0), // needed until https://github.com/flutter/flutter/issues/86170 is fixed + margin: EdgeInsets.all(8.0), + showDuration: Duration(seconds: 3), ))), child: body, ); From ef7dd8748676be0bf2dfbf7e2b70f255a0287992 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Mon, 11 Apr 2022 12:03:54 +0200 Subject: [PATCH 36/38] fix: #304 --- .../UnsupportedDownloadFormatException.dart | 4 ++ lib/pages/GalleryPage.dart | 38 +++++++++++++------ lib/theme/Helpers.dart | 11 +++++- 3 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 lib/exceptions/UnsupportedDownloadFormatException.dart diff --git a/lib/exceptions/UnsupportedDownloadFormatException.dart b/lib/exceptions/UnsupportedDownloadFormatException.dart new file mode 100644 index 0000000..1973eb0 --- /dev/null +++ b/lib/exceptions/UnsupportedDownloadFormatException.dart @@ -0,0 +1,4 @@ +class UnsupportedDownloadFormatException implements Exception { + final message; + const UnsupportedDownloadFormatException(this.message); +} diff --git a/lib/pages/GalleryPage.dart b/lib/pages/GalleryPage.dart index 6a7c42c..cb68d50 100644 --- a/lib/pages/GalleryPage.dart +++ b/lib/pages/GalleryPage.dart @@ -1,4 +1,3 @@ -import 'dart:collection'; import 'dart:io'; import 'dart:typed_data'; @@ -8,16 +7,18 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:fyx/components/post/PostHeroAttachment.dart'; import 'package:fyx/controllers/AnalyticsProvider.dart'; -import 'package:fyx/theme/skin/SkinColors.dart'; -import 'package:fyx/theme/skin/Skin.dart'; -import 'package:path/path.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:sentry/sentry.dart'; +import 'package:fyx/exceptions/UnsupportedDownloadFormatException.dart'; +import 'package:fyx/theme/Helpers.dart'; import 'package:fyx/theme/L.dart'; import 'package:fyx/theme/T.dart'; +import 'package:fyx/theme/skin/Skin.dart'; +import 'package:fyx/theme/skin/SkinColors.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; +import 'package:mime/mime.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:photo_view/photo_view_gallery.dart'; +import 'package:sentry/sentry.dart'; class GalleryPage extends StatefulWidget { @override @@ -133,23 +134,38 @@ class _GalleryPageState extends State { try { PermissionStatus status = await Permission.storage.request(); if (status.isGranted) { + // See https://github.com/lucien144/fyx/issues/304#issuecomment-1094851596 + var appDocDir = await getTemporaryDirectory(); String url = _arguments!.images[_page - 1].image; - String filename = Uri.parse(url).queryParameters['name'] ?? basename(url).split('?')[0].split('#')[0]; - String savePath = appDocDir.path + '/$filename'; - + String savePath = '${appDocDir.path}/${Helpers.uuid(6)}'; await Dio().download(url, savePath); - final result = await ImageGallerySaver.saveFile(savePath, isReturnPathOfIOS: Platform.isIOS); + + File file = new File(savePath); + Uint8List headerBytes = file.readAsBytesSync(); + var ext = extensionFromMime(lookupMimeType(savePath, headerBytes: headerBytes.getRange(0, 20).toList()) ?? ''); + ext = ext == 'jpe' ? 'jpg' : ext; // https://github.com/dart-lang/mime/issues/55 + if (!['jpg', 'png', 'gif', 'heic'].contains(ext)) { + file.delete(); + throw UnsupportedDownloadFormatException('Nelze uložit. Neznámý typ souboru ($ext).'); + } + + file = await file.rename('$savePath.$ext'); + final result = await ImageGallerySaver.saveFile('$savePath.$ext', isReturnPathOfIOS: Platform.isIOS); if (!result['isSuccess']) { throw Error(); } T.success(L.TOAST_IMAGE_SAVE_OK, bg: colors.success); + file.delete(); } else { T.error('Nelze uložit. Povolte ukládání, prosím.', bg: colors.danger); } + } on UnsupportedDownloadFormatException catch (exception) { + T.error(exception.message, bg: colors.danger); } catch (error) { T.error(L.TOAST_IMAGE_SAVE_ERROR, bg: colors.danger); Sentry.captureException(error); + print((error as Error).stackTrace); } finally { setState(() => _saving = false); } diff --git a/lib/theme/Helpers.dart b/lib/theme/Helpers.dart index 8ea10f6..77f6fdb 100644 --- a/lib/theme/Helpers.dart +++ b/lib/theme/Helpers.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:fyx/theme/L.dart'; import 'package:html/parser.dart'; import 'package:intl/intl.dart'; @@ -105,4 +107,11 @@ class Helpers { static String avatarUrl(String username) { return 'https://nyx.cz/${username.substring(0, 1)}/$username.gif'; } -} \ No newline at end of file + + // https://stackoverflow.com/a/61929967/4026345 + static String uuid(int length) { + const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; + Random _rnd = Random(); + return String.fromCharCodes(Iterable.generate(length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length)))); + } +} From b419f8dc03fb795c6a344e401d31959995217d79 Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Tue, 26 Apr 2022 17:28:06 +0200 Subject: [PATCH 37/38] fix: #310 --- lib/pages/LoginPage.dart | 129 ++++++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 43 deletions(-) diff --git a/lib/pages/LoginPage.dart b/lib/pages/LoginPage.dart index 3fb0792..04773e8 100644 --- a/lib/pages/LoginPage.dart +++ b/lib/pages/LoginPage.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -9,9 +7,10 @@ import 'package:fyx/model/Credentials.dart'; import 'package:fyx/model/MainRepository.dart'; import 'package:fyx/model/reponses/LoginResponse.dart'; import 'package:fyx/pages/TutorialPage.dart'; +import 'package:fyx/theme/L.dart'; import 'package:fyx/theme/T.dart'; -import 'package:fyx/theme/skin/SkinColors.dart'; import 'package:fyx/theme/skin/Skin.dart'; +import 'package:fyx/theme/skin/SkinColors.dart'; class LoginPage extends StatefulWidget { @override @@ -24,6 +23,7 @@ class _LoginPageState extends State { late SkinColors colors; bool _isRunning = false; bool _useTokenToLogin = false; + bool _terms = false; @override void initState() { @@ -45,13 +45,21 @@ class _LoginPageState extends State { colors = Skin.of(context).theme.colors; return WillPopScope( - onWillPop: () async => false, - child: Container( - padding: EdgeInsets.all(16), - decoration: BoxDecoration(gradient: colors.gradient), - child: formFactory(), - ), - ); + onWillPop: () async => false, + child: SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: MediaQuery.of(context).size.width, + minHeight: MediaQuery.of(context).size.height, + ), + child: IntrinsicHeight( + child: Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration(gradient: colors.gradient), + child: formFactory(), + ), + )))); } @override @@ -62,11 +70,13 @@ class _LoginPageState extends State { } Widget formFactory() { - final textfieldDecoration = BoxDecoration(borderRadius: BorderRadius.circular(4), color: colors.background, border: Border.all(color: colors.background)); + final textfieldDecoration = + BoxDecoration(borderRadius: BorderRadius.circular(4), color: colors.background, border: Border.all(color: colors.background)); var offset = (MediaQuery.of(context).viewInsets.bottom / 3); return Column( mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, children: [ Container( width: 120, @@ -75,8 +85,10 @@ class _LoginPageState extends State { 'assets/logo.png', color: colors.primary, ), - decoration: - BoxDecoration(color: colors.background, borderRadius: BorderRadius.circular(32), boxShadow: [BoxShadow(color: colors.dark, offset: Offset(0, 0), blurRadius: 16)]), + decoration: BoxDecoration( + color: colors.background, + borderRadius: BorderRadius.circular(32), + boxShadow: [BoxShadow(color: colors.dark, offset: Offset(0, 0), blurRadius: 16)]), ), AnimatedPadding( padding: EdgeInsets.only(top: 128 - offset), @@ -114,9 +126,40 @@ class _LoginPageState extends State { duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: Container( - child: this._buildButton(context), + child: Column( + children: [ + this._buildButton(context), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CupertinoSwitch( + activeColor: colors.highlight, + thumbColor: colors.background, + trackColor: colors.primary, + onChanged: (bool? value) => setState(() => _terms = value ?? false), + value: _terms, + ), + GestureDetector( + child: Text( + 'Souhlasím s podmínkami užití.', + style: TextStyle(color: colors.light), + ), + onTap: () => setState(() => _terms = !_terms), + ) + ], + ), + GestureDetector( + child: Text( + '${L.TERMS} ↗', + style: TextStyle(decoration: TextDecoration.underline, color: colors.light, fontSize: 13), + ), + onTap: () => T.openLink('https://nyx.cz/terms'), + ) + ], + ), ), - ) + ), ], ); } @@ -127,40 +170,40 @@ class _LoginPageState extends State { return Container( width: 200, child: CupertinoButton( + disabledColor: colors.light.withOpacity(.3), child: _isRunning ? CupertinoActivityIndicator() : Text( 'Přihlásit', style: TextStyle(color: colors.primary), ), - onPressed: () { - if (_isRunning) { - return; - } - - setState(() => _isRunning = true); - - if (_useTokenToLogin && _tokenController.text.length > 0) { - ApiController().setCredentials(Credentials(_loginController.text, _tokenController.text)).then((Credentials? credentials) { - if (credentials != null) { - // TODO: Refactor 👇? This is edge case usage... - ApiController().provider.setCredentials(credentials); - MainRepository().credentials = credentials; - Navigator.of(context).pushNamed('/home'); - } - }).whenComplete(() { - setState(() => _isRunning = false); - }); - return; - } - - ApiController().login(_loginController.text).then((LoginResponse response) { - Navigator.of(context).pushNamed('/token', arguments: new TutorialPageArguments(token: response.authCode, username: _loginController.text)); - }).catchError((error) { - print(error); - T.error(error.toString(), bg: colors.danger); - }).whenComplete(() => setState(() => _isRunning = false)); - }, + onPressed: _isRunning || !_terms || _loginController.text.length == 0 + ? null + : () { + setState(() => _isRunning = true); + + if (_useTokenToLogin && _tokenController.text.length > 0) { + ApiController().setCredentials(Credentials(_loginController.text, _tokenController.text)).then((Credentials? credentials) { + if (credentials != null) { + // TODO: Refactor 👇? This is edge case usage... + ApiController().provider.setCredentials(credentials); + MainRepository().credentials = credentials; + Navigator.of(context).pushNamed('/home'); + } + }).whenComplete(() { + setState(() => _isRunning = false); + }); + return; + } + + ApiController().login(_loginController.text).then((LoginResponse response) { + Navigator.of(context) + .pushNamed('/token', arguments: new TutorialPageArguments(token: response.authCode, username: _loginController.text)); + }).catchError((error) { + print(error); + T.error(error.toString(), bg: colors.danger); + }).whenComplete(() => setState(() => _isRunning = false)); + }, color: colors.background, ), ); From f9d5e1033316777e4f5f3da72af1d2229dcdf6ef Mon Sep 17 00:00:00 2001 From: Jan Blasko Date: Tue, 26 Apr 2022 17:49:15 +0200 Subject: [PATCH 38/38] chore: build files --- ios/Podfile.lock | 26 +++++----- ios/Runner.xcodeproj/project.pbxproj | 4 +- ios/Runner/Base.lproj/LaunchScreen.storyboard | 4 +- pubspec.lock | 49 ++++++++++++++++--- pubspec.yaml | 2 +- 5 files changed, 60 insertions(+), 25 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7fdb024..a17c8ad 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -44,12 +44,12 @@ PODS: - FirebaseCoreDiagnostics (~> 8.0) - GoogleUtilities/Environment (~> 7.6) - GoogleUtilities/Logger (~> 7.6) - - FirebaseCoreDiagnostics (8.14.0): + - FirebaseCoreDiagnostics (8.15.0): - GoogleDataTransport (~> 9.1) - GoogleUtilities/Environment (~> 7.7) - GoogleUtilities/Logger (~> 7.7) - nanopb (~> 2.30908.0) - - FirebaseInstallations (8.14.0): + - FirebaseInstallations (8.15.0): - FirebaseCore (~> 8.0) - GoogleUtilities/Environment (~> 7.7) - GoogleUtilities/UserDefaults (~> 7.7) @@ -115,7 +115,7 @@ PODS: - GoogleUtilities/Logger - image_gallery_saver (1.5.0): - Flutter - - image_picker (0.0.1): + - image_picker_ios (0.0.1): - Flutter - nanopb (2.30908.0): - nanopb/decode (= 2.30908.0) @@ -128,9 +128,9 @@ PODS: - Flutter - path_provider_ios (0.0.1): - Flutter - - permission_handler_apple (9.0.2): + - permission_handler_apple (9.0.4): - Flutter - - PromisesObjC (2.0.0) + - PromisesObjC (2.1.0) - Sentry (7.9.0): - Sentry/Core (= 7.9.0) - Sentry/Core (7.9.0) @@ -163,7 +163,7 @@ DEPENDENCIES: - Flutter (from `Flutter`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`) - - image_picker (from `.symlinks/plugins/image_picker/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) @@ -209,8 +209,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/fluttertoast/ios" image_gallery_saver: :path: ".symlinks/plugins/image_gallery_saver/ios" - image_picker: - :path: ".symlinks/plugins/image_picker/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" package_info: :path: ".symlinks/plugins/package_info/ios" package_info_plus: @@ -244,8 +244,8 @@ SPEC CHECKSUMS: firebase_messaging: dff5cd08781ee1de988565a83c977e435405cd7e FirebaseAnalytics: 4ab446ce08a3fe52e8a4303dd997cf26276bf968 FirebaseCore: 599ee609343eaf4941bd188f85e3aa077ffe325b - FirebaseCoreDiagnostics: fd0c8490f34287229c1d6c103d3a55f81ec85712 - FirebaseInstallations: 7d1d967a307c12f1aadd76844fc321cef699b1ce + FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb + FirebaseInstallations: 40bd9054049b2eae9a2c38ef1c3dd213df3605cd FirebaseMessaging: 82c4a48638f53f7b184f3cc9f6cd2cbe533ab316 Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037 @@ -254,13 +254,13 @@ SPEC CHECKSUMS: GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940 GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2 - image_picker: 541dcbb3b9cf32d87eacbd957845d8651d6c62c3 + image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 - permission_handler_apple: d21b38e1a4b2e041c63af9568f9165e114e507a6 - PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 + permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + PromisesObjC: 99b6f43f9e1044bd87a95a60beff28c2c44ddb72 Sentry: 2f7e91f247cfb05b05bd01e0b5d0692557a7687b sentry_flutter: 7c3cb050dc23563a4ea5db438c83afdb460a2ae6 share: 0b2c3e82132f5888bccca3351c504d0003b3b410 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index cfba592..821fde7 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -268,7 +268,7 @@ "${BUILT_PRODUCTS_DIR}/device_info/device_info.framework", "${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework", "${BUILT_PRODUCTS_DIR}/image_gallery_saver/image_gallery_saver.framework", - "${BUILT_PRODUCTS_DIR}/image_picker/image_picker.framework", + "${BUILT_PRODUCTS_DIR}/image_picker_ios/image_picker_ios.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", "${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework", @@ -297,7 +297,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_gallery_saver.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework", diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard index 29dd51d..5fd13ed 100644 --- a/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -1,9 +1,9 @@ - + - + diff --git a/pubspec.lock b/pubspec.lock index 02ffd69..9fae403 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -404,7 +404,7 @@ packages: name: flutter_html url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" + version: "2.2.1" flutter_launcher_icons: dependency: "direct dev" description: @@ -475,7 +475,7 @@ packages: name: fluttertoast url: "https://pub.dartlang.org" source: hosted - version: "8.0.8" + version: "8.0.9" frontend_server_client: dependency: transitive description: @@ -580,7 +580,14 @@ packages: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.8.4+4" + version: "0.8.5" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.4+11" image_picker_for_web: dependency: transitive description: @@ -588,6 +595,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.4+11" image_picker_platform_interface: dependency: transitive description: @@ -825,7 +839,21 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "8.1.6" + version: "9.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.2+1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.4" permission_handler_platform_interface: dependency: transitive description: @@ -833,6 +861,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.7.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" petitparser: dependency: transitive description: @@ -923,7 +958,7 @@ packages: name: sentry_flutter url: "https://pub.dartlang.org" source: hosted - version: "6.1.0" + version: "6.3.0" share: dependency: "direct main" description: @@ -1201,7 +1236,7 @@ packages: name: video_player url: "https://pub.dartlang.org" source: hosted - version: "2.2.18" + version: "2.2.19" video_player_android: dependency: transitive description: @@ -1315,5 +1350,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.14.0 <3.0.0" + dart: ">=2.15.0 <3.0.0" flutter: ">=2.10.2" diff --git a/pubspec.yaml b/pubspec.yaml index a03bce4..99d2049 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: A new Flutter project. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.8.2+45 +version: 0.8.3+57 environment: sdk: ">=2.12.0 <3.0.0"