From 9ebd731e72cc3804fb831a5ce4e8133010d4252c Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Sun, 24 Oct 2021 22:22:17 +0200 Subject: [PATCH 1/5] update deps --- pubspec.lock | 10 +++++----- pubspec.yaml | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 9a5a53b..59d3134 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -300,7 +300,7 @@ packages: name: freezed_annotation url: "https://pub.dartlang.org" source: hosted - version: "0.14.3" + version: "0.15.0" frontend_server_client: dependency: transitive description: @@ -412,14 +412,14 @@ packages: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "4.3.0" json_serializable: dependency: "direct dev" description: name: json_serializable url: "https://pub.dartlang.org" source: hosted - version: "5.0.0" + version: "6.0.1" keyboard_dismisser: dependency: "direct main" description: @@ -440,7 +440,7 @@ packages: name: lemmy_api_client url: "https://pub.dartlang.org" source: hosted - version: "0.16.0" + version: "0.17.0" logging: dependency: "direct main" description: @@ -790,7 +790,7 @@ packages: name: source_helper url: "https://pub.dartlang.org" source: hosted - version: "1.2.1" + version: "1.3.0" source_span: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 96abd48..d265423 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,12 +49,12 @@ dependencies: # utils timeago: ^3.0.2 fuzzy: ^0.4.0-nullsafety.0 - lemmy_api_client: ^0.16.0 + lemmy_api_client: ^0.17.0 intl: ^0.17.0 matrix4_transform: ^2.0.0 json_annotation: ^4.1.0 keyboard_dismisser: ^2.0.0 - freezed_annotation: ^0.14.3 + freezed_annotation: ^0.15.0 logging: ^1.0.1 flutter: @@ -70,7 +70,7 @@ dev_dependencies: flutter_test: sdk: flutter flutter_launcher_icons: ^0.9.2 - json_serializable: ^5.0.0 + json_serializable: ^6.0.0 build_runner: ^2.1.2 mobx_codegen: ^2.0.2 freezed: ^0.14.1+2 From 5dbb621582709aa6cba9cc744d9d0bbc50d0891b Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Sun, 24 Oct 2021 22:36:41 +0200 Subject: [PATCH 2/5] add reporting functionality --- lib/widgets/comment/comment.dart | 6 ++- .../comment/comment_more_menu_button.dart | 19 ++++++++++ lib/widgets/comment/comment_store.dart | 15 ++++++++ lib/widgets/post/post.dart | 6 ++- lib/widgets/post/post_more_menu.dart | 19 ++++++++++ lib/widgets/post/post_store.dart | 15 ++++++++ lib/widgets/report_dialog.dart | 38 +++++++++++++++++++ 7 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 lib/widgets/report_dialog.dart diff --git a/lib/widgets/comment/comment.dart b/lib/widgets/comment/comment.dart index 60907b2..bf42abc 100644 --- a/lib/widgets/comment/comment.dart +++ b/lib/widgets/comment/comment.dart @@ -102,7 +102,11 @@ class CommentWidget extends StatelessWidget { asyncStore: context.read().deletingState, child: AsyncStoreListener( asyncStore: context.read().savingState, - child: const _CommentWidget(), + child: AsyncStoreListener( + asyncStore: context.read().reportingState, + successMessageBuilder: (context, data) => 'Comment reported', + child: const _CommentWidget(), + ), ), ), ), diff --git a/lib/widgets/comment/comment_more_menu_button.dart b/lib/widgets/comment/comment_more_menu_button.dart index 9dba60c..eabe9f6 100644 --- a/lib/widgets/comment/comment_more_menu_button.dart +++ b/lib/widgets/comment/comment_more_menu_button.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:lemmy_api_client/v3.dart'; @@ -10,6 +12,7 @@ import '../../util/observer_consumers.dart'; import '../../util/share.dart'; import '../bottom_modal.dart'; import '../markdown_mode_icon.dart'; +import '../report_dialog.dart'; import '../tile_action.dart'; import '../write_comment.dart'; import 'comment.dart'; @@ -155,6 +158,22 @@ class _CommentMoreMenuPopup extends HookWidget { store.block(token); }), ), + ListTile( + leading: store.reportingState.isLoading + ? const CircularProgressIndicator.adaptive() + : const Icon(Icons.flag), + title: const Text('Report'), + onTap: () { + if (store.reportingState.isLoading) return; + Navigator.of(context).pop(); + loggedInAction((token) async { + final reason = await ReportDialog.show(context); + if (reason != null) { + unawaited(store.report(token, reason)); + } + })(); + }, + ), ListTile( leading: const Icon(Icons.info_outline), title: const Text('Nerd stuff'), diff --git a/lib/widgets/comment/comment_store.dart b/lib/widgets/comment/comment_store.dart index 685dfc3..f04575a 100644 --- a/lib/widgets/comment/comment_store.dart +++ b/lib/widgets/comment/comment_store.dart @@ -36,6 +36,7 @@ abstract class _CommentStore with Store { final markPersonMentionAsReadState = AsyncStore(); final markAsReadState = AsyncStore(); final blockingState = AsyncStore(); + final reportingState = AsyncStore(); @computed bool get isMine => @@ -76,6 +77,20 @@ abstract class _CommentStore with Store { collapsed = !collapsed; } + @action + Future report(Jwt token, String reason) async { + if (reason.trim().isEmpty) throw ArgumentError('reason must not be empty'); + + await reportingState.runLemmy( + comment.instanceHost, + CreateCommentReport( + commentId: comment.comment.id, + reason: reason, + auth: token.raw, + ), + ); + } + @action Future delete(Jwt token) async { final result = await deletingState.runLemmy( diff --git a/lib/widgets/post/post.dart b/lib/widgets/post/post.dart index 442fd77..3f3eab7 100644 --- a/lib/widgets/post/post.dart +++ b/lib/widgets/post/post.dart @@ -41,7 +41,11 @@ class PostTile extends StatelessWidget { final name = state.personView.person.preferredName; return state.blocked ? '$name blocked' : '$name unblocked'; }, - child: const _Post(), + child: AsyncStoreListener( + asyncStore: context.read().reportingState, + successMessageBuilder: (context, data) => 'Post reported', + child: const _Post(), + ), ), ), ); diff --git a/lib/widgets/post/post_more_menu.dart b/lib/widgets/post/post_more_menu.dart index a7a1d61..161be6c 100644 --- a/lib/widgets/post/post_more_menu.dart +++ b/lib/widgets/post/post_more_menu.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -12,6 +14,7 @@ import '../../util/icons.dart'; import '../../util/observer_consumers.dart'; import '../bottom_modal.dart'; import '../info_table_popup.dart'; +import '../report_dialog.dart'; import 'post_store.dart'; class PostMoreMenuButton extends StatelessWidget { @@ -121,6 +124,22 @@ class PostMoreMenu extends HookWidget { ); }, ), + ListTile( + leading: store.reportingState.isLoading + ? const CircularProgressIndicator.adaptive() + : const Icon(Icons.flag), + title: const Text('Report'), + onTap: () { + if (store.reportingState.isLoading) return; + Navigator.of(context).pop(); + loggedInAction((token) async { + final reason = await ReportDialog.show(context); + if (reason != null) { + unawaited(store.report(token, reason)); + } + })(); + }, + ), ListTile( leading: const Icon(Icons.info_outline), title: const Text('Nerd stuff'), diff --git a/lib/widgets/post/post_store.dart b/lib/widgets/post/post_store.dart index 48cb524..daa0eee 100644 --- a/lib/widgets/post/post_store.dart +++ b/lib/widgets/post/post_store.dart @@ -14,6 +14,7 @@ abstract class _PostStore with Store { final votingState = AsyncStore(); final savingState = AsyncStore(); final userBlockingState = AsyncStore(); + final reportingState = AsyncStore(); @observable PostView postView; @@ -55,6 +56,20 @@ abstract class _PostStore with Store { if (result != null) postView = result; } + @action + Future report(Jwt token, String reason) async { + if (reason.trim().isEmpty) throw ArgumentError('reason must not be empty'); + + await reportingState.runLemmy( + postView.instanceHost, + CreatePostReport( + postId: postView.post.id, + reason: reason, + auth: token.raw, + ), + ); + } + @action Future blockUser(Jwt token) async { final result = await userBlockingState.runLemmy( diff --git a/lib/widgets/report_dialog.dart b/lib/widgets/report_dialog.dart new file mode 100644 index 0000000..fc70922 --- /dev/null +++ b/lib/widgets/report_dialog.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +class ReportDialog extends HookWidget { + const ReportDialog(); + + @override + Widget build(BuildContext context) { + final controller = useListenable(useTextEditingController()); + return AlertDialog( + title: const Text('Report'), + content: TextField( + autofocus: true, + controller: controller, + decoration: const InputDecoration( + label: Text('reason'), + ), + minLines: 1, + maxLines: 3, + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('cancel'), + ), + TextButton( + onPressed: controller.text.trim().isEmpty + ? null + : () => Navigator.of(context).pop(controller.text.trim()), + child: const Text('report'), + ), + ], + ); + } + + static Future show(BuildContext context) async => + showDialog(context: context, builder: (context) => const ReportDialog()); +} From ae235069ba64c171845abe67995ae3e53b90ce16 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Sun, 24 Oct 2021 22:48:04 +0200 Subject: [PATCH 3/5] update dialog border radius to fit the whole app --- lib/theme.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/theme.dart b/lib/theme.dart index 07e3b3b..6897fe4 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -85,6 +85,11 @@ ThemeData _themeFactory({bool dark = false, bool amoled = false}) { ), ), ), + dialogTheme: DialogTheme( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), ); } From 8703246c657005352b7697609607834277d9d124 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Sun, 24 Oct 2021 23:11:44 +0200 Subject: [PATCH 4/5] upgrade dependency & build runner build --- lib/stores/config_store.g.dart | 41 ++---------------------- lib/widgets/comment/comment_store.g.dart | 7 ++++ lib/widgets/post/post_store.g.dart | 7 ++++ pubspec.yaml | 2 +- 4 files changed, 17 insertions(+), 40 deletions(-) diff --git a/lib/stores/config_store.g.dart b/lib/stores/config_store.g.dart index 1f82534..63510ea 100644 --- a/lib/stores/config_store.g.dart +++ b/lib/stores/config_store.g.dart @@ -7,8 +7,8 @@ part of 'config_store.dart'; // ************************************************************************** ConfigStore _$ConfigStoreFromJson(Map json) => ConfigStore() - ..theme = _$enumDecodeNullable(_$ThemeModeEnumMap, json['theme']) ?? - ThemeMode.system + ..theme = + $enumDecodeNullable(_$ThemeModeEnumMap, json['theme']) ?? ThemeMode.system ..amoledDarkMode = json['amoledDarkMode'] as bool? ?? false ..locale = LocaleSerde.fromJson(json['locale'] as String?) ..showAvatars = json['showAvatars'] as bool? ?? true @@ -28,43 +28,6 @@ Map _$ConfigStoreToJson(ConfigStore instance) => 'defaultListingType': instance.defaultListingType, }; -K _$enumDecode( - Map enumValues, - Object? source, { - K? unknownValue, -}) { - if (source == null) { - throw ArgumentError( - 'A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}', - ); - } - - return enumValues.entries.singleWhere( - (e) => e.value == source, - orElse: () { - if (unknownValue == null) { - throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}', - ); - } - return MapEntry(unknownValue, enumValues.values.first); - }, - ).key; -} - -K? _$enumDecodeNullable( - Map enumValues, - dynamic source, { - K? unknownValue, -}) { - if (source == null) { - return null; - } - return _$enumDecode(enumValues, source, unknownValue: unknownValue); -} - const _$ThemeModeEnumMap = { ThemeMode.system: 'system', ThemeMode.light: 'light', diff --git a/lib/widgets/comment/comment_store.g.dart b/lib/widgets/comment/comment_store.g.dart index 371a9b4..ecaf626 100644 --- a/lib/widgets/comment/comment_store.g.dart +++ b/lib/widgets/comment/comment_store.g.dart @@ -88,6 +88,13 @@ mixin _$CommentStore on _CommentStore, Store { }); } + final _$reportAsyncAction = AsyncAction('_CommentStore.report'); + + @override + Future report(Jwt token, String reason) { + return _$reportAsyncAction.run(() => super.report(token, reason)); + } + final _$deleteAsyncAction = AsyncAction('_CommentStore.delete'); @override diff --git a/lib/widgets/post/post_store.g.dart b/lib/widgets/post/post_store.g.dart index f17f89f..583c01e 100644 --- a/lib/widgets/post/post_store.g.dart +++ b/lib/widgets/post/post_store.g.dart @@ -45,6 +45,13 @@ mixin _$PostStore on _PostStore, Store { return _$saveAsyncAction.run(() => super.save(token)); } + final _$reportAsyncAction = AsyncAction('_PostStore.report'); + + @override + Future report(Jwt token, String reason) { + return _$reportAsyncAction.run(() => super.report(token, reason)); + } + final _$blockUserAsyncAction = AsyncAction('_PostStore.blockUser'); @override diff --git a/pubspec.yaml b/pubspec.yaml index d265423..92eb9c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,7 +52,7 @@ dependencies: lemmy_api_client: ^0.17.0 intl: ^0.17.0 matrix4_transform: ^2.0.0 - json_annotation: ^4.1.0 + json_annotation: ^4.3.0 keyboard_dismisser: ^2.0.0 freezed_annotation: ^0.15.0 logging: ^1.0.1 From 725807c75b0e94c5ba058aabc24151b256959c39 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 25 Oct 2021 19:52:29 +0200 Subject: [PATCH 5/5] CR fixes --- .../comment/comment_more_menu_button.dart | 23 +++++++++---------- lib/widgets/post/post_more_menu.dart | 23 +++++++++---------- lib/widgets/report_dialog.dart | 1 + 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/lib/widgets/comment/comment_more_menu_button.dart b/lib/widgets/comment/comment_more_menu_button.dart index eabe9f6..9e68c31 100644 --- a/lib/widgets/comment/comment_more_menu_button.dart +++ b/lib/widgets/comment/comment_more_menu_button.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:lemmy_api_client/v3.dart'; @@ -163,16 +161,17 @@ class _CommentMoreMenuPopup extends HookWidget { ? const CircularProgressIndicator.adaptive() : const Icon(Icons.flag), title: const Text('Report'), - onTap: () { - if (store.reportingState.isLoading) return; - Navigator.of(context).pop(); - loggedInAction((token) async { - final reason = await ReportDialog.show(context); - if (reason != null) { - unawaited(store.report(token, reason)); - } - })(); - }, + onTap: store.reportingState.isLoading + ? null + : () { + Navigator.of(context).pop(); + loggedInAction((token) async { + final reason = await ReportDialog.show(context); + if (reason != null) { + await store.report(token, reason); + } + })(); + }, ), ListTile( leading: const Icon(Icons.info_outline), diff --git a/lib/widgets/post/post_more_menu.dart b/lib/widgets/post/post_more_menu.dart index 161be6c..b3c3321 100644 --- a/lib/widgets/post/post_more_menu.dart +++ b/lib/widgets/post/post_more_menu.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -129,16 +127,17 @@ class PostMoreMenu extends HookWidget { ? const CircularProgressIndicator.adaptive() : const Icon(Icons.flag), title: const Text('Report'), - onTap: () { - if (store.reportingState.isLoading) return; - Navigator.of(context).pop(); - loggedInAction((token) async { - final reason = await ReportDialog.show(context); - if (reason != null) { - unawaited(store.report(token, reason)); - } - })(); - }, + onTap: store.reportingState.isLoading + ? null + : () { + Navigator.of(context).pop(); + loggedInAction((token) async { + final reason = await ReportDialog.show(context); + if (reason != null) { + await store.report(token, reason); + } + })(); + }, ), ListTile( leading: const Icon(Icons.info_outline), diff --git a/lib/widgets/report_dialog.dart b/lib/widgets/report_dialog.dart index fc70922..541f9b0 100644 --- a/lib/widgets/report_dialog.dart +++ b/lib/widgets/report_dialog.dart @@ -7,6 +7,7 @@ class ReportDialog extends HookWidget { @override Widget build(BuildContext context) { final controller = useListenable(useTextEditingController()); + return AlertDialog( title: const Text('Report'), content: TextField(