Merge pull request #272 from krawieck/feature/report

This commit is contained in:
Filip Krawczyk 2021-10-25 20:53:21 +02:00 committed by GitHub
commit 8f34591111
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 145 additions and 50 deletions

View File

@ -7,8 +7,8 @@ part of 'config_store.dart';
// **************************************************************************
ConfigStore _$ConfigStoreFromJson(Map<String, dynamic> 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<String, dynamic> _$ConfigStoreToJson(ConfigStore instance) =>
'defaultListingType': instance.defaultListingType,
};
K _$enumDecode<K, V>(
Map<K, V> 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<K, V>(
Map<K, V> enumValues,
dynamic source, {
K? unknownValue,
}) {
if (source == null) {
return null;
}
return _$enumDecode<K, V>(enumValues, source, unknownValue: unknownValue);
}
const _$ThemeModeEnumMap = {
ThemeMode.system: 'system',
ThemeMode.light: 'light',

View File

@ -85,6 +85,11 @@ ThemeData _themeFactory({bool dark = false, bool amoled = false}) {
),
),
),
dialogTheme: DialogTheme(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
);
}

View File

@ -102,11 +102,15 @@ class CommentWidget extends StatelessWidget {
asyncStore: context.read<CommentStore>().deletingState,
child: AsyncStoreListener(
asyncStore: context.read<CommentStore>().savingState,
child: AsyncStoreListener<CommentReportView>(
asyncStore: context.read<CommentStore>().reportingState,
successMessageBuilder: (context, data) => 'Comment reported',
child: const _CommentWidget(),
),
),
),
),
),
);
}
}

View File

@ -10,6 +10,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 +156,23 @@ class _CommentMoreMenuPopup extends HookWidget {
store.block(token);
}),
),
ListTile(
leading: store.reportingState.isLoading
? const CircularProgressIndicator.adaptive()
: const Icon(Icons.flag),
title: const Text('Report'),
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),
title: const Text('Nerd stuff'),

View File

@ -36,6 +36,7 @@ abstract class _CommentStore with Store {
final markPersonMentionAsReadState = AsyncStore<PersonMentionView>();
final markAsReadState = AsyncStore<FullCommentView>();
final blockingState = AsyncStore<BlockedPerson>();
final reportingState = AsyncStore<CommentReportView>();
@computed
bool get isMine =>
@ -76,6 +77,20 @@ abstract class _CommentStore with Store {
collapsed = !collapsed;
}
@action
Future<void> 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<void> delete(Jwt token) async {
final result = await deletingState.runLemmy(

View File

@ -88,6 +88,13 @@ mixin _$CommentStore on _CommentStore, Store {
});
}
final _$reportAsyncAction = AsyncAction('_CommentStore.report');
@override
Future<void> report(Jwt token, String reason) {
return _$reportAsyncAction.run(() => super.report(token, reason));
}
final _$deleteAsyncAction = AsyncAction('_CommentStore.delete');
@override

View File

@ -41,9 +41,13 @@ class PostTile extends StatelessWidget {
final name = state.personView.person.preferredName;
return state.blocked ? '$name blocked' : '$name unblocked';
},
child: AsyncStoreListener<PostReportView>(
asyncStore: context.read<PostStore>().reportingState,
successMessageBuilder: (context, data) => 'Post reported',
child: const _Post(),
),
),
),
);
},
);

View File

@ -12,6 +12,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 +122,23 @@ class PostMoreMenu extends HookWidget {
);
},
),
ListTile(
leading: store.reportingState.isLoading
? const CircularProgressIndicator.adaptive()
: const Icon(Icons.flag),
title: const Text('Report'),
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),
title: const Text('Nerd stuff'),

View File

@ -14,6 +14,7 @@ abstract class _PostStore with Store {
final votingState = AsyncStore<PostView>();
final savingState = AsyncStore<PostView>();
final userBlockingState = AsyncStore<BlockedPerson>();
final reportingState = AsyncStore<PostReportView>();
@observable
PostView postView;
@ -55,6 +56,20 @@ abstract class _PostStore with Store {
if (result != null) postView = result;
}
@action
Future<void> 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<void> blockUser(Jwt token) async {
final result = await userBlockingState.runLemmy(

View File

@ -45,6 +45,13 @@ mixin _$PostStore on _PostStore, Store {
return _$saveAsyncAction.run(() => super.save(token));
}
final _$reportAsyncAction = AsyncAction('_PostStore.report');
@override
Future<void> report(Jwt token, String reason) {
return _$reportAsyncAction.run(() => super.report(token, reason));
}
final _$blockUserAsyncAction = AsyncAction('_PostStore.blockUser');
@override

View File

@ -0,0 +1,39 @@
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<String?> show(BuildContext context) async =>
showDialog(context: context, builder: (context) => const ReportDialog());
}

View File

@ -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:

View File

@ -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
json_annotation: ^4.3.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