Merge pull request #272 from krawieck/feature/report
This commit is contained in:
commit
8f34591111
|
@ -7,8 +7,8 @@ part of 'config_store.dart';
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
ConfigStore _$ConfigStoreFromJson(Map<String, dynamic> json) => ConfigStore()
|
ConfigStore _$ConfigStoreFromJson(Map<String, dynamic> json) => ConfigStore()
|
||||||
..theme = _$enumDecodeNullable(_$ThemeModeEnumMap, json['theme']) ??
|
..theme =
|
||||||
ThemeMode.system
|
$enumDecodeNullable(_$ThemeModeEnumMap, json['theme']) ?? ThemeMode.system
|
||||||
..amoledDarkMode = json['amoledDarkMode'] as bool? ?? false
|
..amoledDarkMode = json['amoledDarkMode'] as bool? ?? false
|
||||||
..locale = LocaleSerde.fromJson(json['locale'] as String?)
|
..locale = LocaleSerde.fromJson(json['locale'] as String?)
|
||||||
..showAvatars = json['showAvatars'] as bool? ?? true
|
..showAvatars = json['showAvatars'] as bool? ?? true
|
||||||
|
@ -28,43 +28,6 @@ Map<String, dynamic> _$ConfigStoreToJson(ConfigStore instance) =>
|
||||||
'defaultListingType': instance.defaultListingType,
|
'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 = {
|
const _$ThemeModeEnumMap = {
|
||||||
ThemeMode.system: 'system',
|
ThemeMode.system: 'system',
|
||||||
ThemeMode.light: 'light',
|
ThemeMode.light: 'light',
|
||||||
|
|
|
@ -85,6 +85,11 @@ ThemeData _themeFactory({bool dark = false, bool amoled = false}) {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
dialogTheme: DialogTheme(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,11 @@ class CommentWidget extends StatelessWidget {
|
||||||
asyncStore: context.read<CommentStore>().deletingState,
|
asyncStore: context.read<CommentStore>().deletingState,
|
||||||
child: AsyncStoreListener(
|
child: AsyncStoreListener(
|
||||||
asyncStore: context.read<CommentStore>().savingState,
|
asyncStore: context.read<CommentStore>().savingState,
|
||||||
child: const _CommentWidget(),
|
child: AsyncStoreListener<CommentReportView>(
|
||||||
|
asyncStore: context.read<CommentStore>().reportingState,
|
||||||
|
successMessageBuilder: (context, data) => 'Comment reported',
|
||||||
|
child: const _CommentWidget(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -10,6 +10,7 @@ import '../../util/observer_consumers.dart';
|
||||||
import '../../util/share.dart';
|
import '../../util/share.dart';
|
||||||
import '../bottom_modal.dart';
|
import '../bottom_modal.dart';
|
||||||
import '../markdown_mode_icon.dart';
|
import '../markdown_mode_icon.dart';
|
||||||
|
import '../report_dialog.dart';
|
||||||
import '../tile_action.dart';
|
import '../tile_action.dart';
|
||||||
import '../write_comment.dart';
|
import '../write_comment.dart';
|
||||||
import 'comment.dart';
|
import 'comment.dart';
|
||||||
|
@ -155,6 +156,23 @@ class _CommentMoreMenuPopup extends HookWidget {
|
||||||
store.block(token);
|
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(
|
ListTile(
|
||||||
leading: const Icon(Icons.info_outline),
|
leading: const Icon(Icons.info_outline),
|
||||||
title: const Text('Nerd stuff'),
|
title: const Text('Nerd stuff'),
|
||||||
|
|
|
@ -36,6 +36,7 @@ abstract class _CommentStore with Store {
|
||||||
final markPersonMentionAsReadState = AsyncStore<PersonMentionView>();
|
final markPersonMentionAsReadState = AsyncStore<PersonMentionView>();
|
||||||
final markAsReadState = AsyncStore<FullCommentView>();
|
final markAsReadState = AsyncStore<FullCommentView>();
|
||||||
final blockingState = AsyncStore<BlockedPerson>();
|
final blockingState = AsyncStore<BlockedPerson>();
|
||||||
|
final reportingState = AsyncStore<CommentReportView>();
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get isMine =>
|
bool get isMine =>
|
||||||
|
@ -76,6 +77,20 @@ abstract class _CommentStore with Store {
|
||||||
collapsed = !collapsed;
|
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
|
@action
|
||||||
Future<void> delete(Jwt token) async {
|
Future<void> delete(Jwt token) async {
|
||||||
final result = await deletingState.runLemmy(
|
final result = await deletingState.runLemmy(
|
||||||
|
|
|
@ -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');
|
final _$deleteAsyncAction = AsyncAction('_CommentStore.delete');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -41,7 +41,11 @@ class PostTile extends StatelessWidget {
|
||||||
final name = state.personView.person.preferredName;
|
final name = state.personView.person.preferredName;
|
||||||
return state.blocked ? '$name blocked' : '$name unblocked';
|
return state.blocked ? '$name blocked' : '$name unblocked';
|
||||||
},
|
},
|
||||||
child: const _Post(),
|
child: AsyncStoreListener<PostReportView>(
|
||||||
|
asyncStore: context.read<PostStore>().reportingState,
|
||||||
|
successMessageBuilder: (context, data) => 'Post reported',
|
||||||
|
child: const _Post(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,6 +12,7 @@ import '../../util/icons.dart';
|
||||||
import '../../util/observer_consumers.dart';
|
import '../../util/observer_consumers.dart';
|
||||||
import '../bottom_modal.dart';
|
import '../bottom_modal.dart';
|
||||||
import '../info_table_popup.dart';
|
import '../info_table_popup.dart';
|
||||||
|
import '../report_dialog.dart';
|
||||||
import 'post_store.dart';
|
import 'post_store.dart';
|
||||||
|
|
||||||
class PostMoreMenuButton extends StatelessWidget {
|
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(
|
ListTile(
|
||||||
leading: const Icon(Icons.info_outline),
|
leading: const Icon(Icons.info_outline),
|
||||||
title: const Text('Nerd stuff'),
|
title: const Text('Nerd stuff'),
|
||||||
|
|
|
@ -14,6 +14,7 @@ abstract class _PostStore with Store {
|
||||||
final votingState = AsyncStore<PostView>();
|
final votingState = AsyncStore<PostView>();
|
||||||
final savingState = AsyncStore<PostView>();
|
final savingState = AsyncStore<PostView>();
|
||||||
final userBlockingState = AsyncStore<BlockedPerson>();
|
final userBlockingState = AsyncStore<BlockedPerson>();
|
||||||
|
final reportingState = AsyncStore<PostReportView>();
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
PostView postView;
|
PostView postView;
|
||||||
|
@ -55,6 +56,20 @@ abstract class _PostStore with Store {
|
||||||
if (result != null) postView = result;
|
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
|
@action
|
||||||
Future<void> blockUser(Jwt token) async {
|
Future<void> blockUser(Jwt token) async {
|
||||||
final result = await userBlockingState.runLemmy(
|
final result = await userBlockingState.runLemmy(
|
||||||
|
|
|
@ -45,6 +45,13 @@ mixin _$PostStore on _PostStore, Store {
|
||||||
return _$saveAsyncAction.run(() => super.save(token));
|
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');
|
final _$blockUserAsyncAction = AsyncAction('_PostStore.blockUser');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
10
pubspec.lock
10
pubspec.lock
|
@ -300,7 +300,7 @@ packages:
|
||||||
name: freezed_annotation
|
name: freezed_annotation
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.14.3"
|
version: "0.15.0"
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -412,14 +412,14 @@ packages:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.3.0"
|
||||||
json_serializable:
|
json_serializable:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: json_serializable
|
name: json_serializable
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "6.0.1"
|
||||||
keyboard_dismisser:
|
keyboard_dismisser:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -440,7 +440,7 @@ packages:
|
||||||
name: lemmy_api_client
|
name: lemmy_api_client
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.16.0"
|
version: "0.17.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -790,7 +790,7 @@ packages:
|
||||||
name: source_helper
|
name: source_helper
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.3.0"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -49,12 +49,12 @@ dependencies:
|
||||||
# utils
|
# utils
|
||||||
timeago: ^3.0.2
|
timeago: ^3.0.2
|
||||||
fuzzy: ^0.4.0-nullsafety.0
|
fuzzy: ^0.4.0-nullsafety.0
|
||||||
lemmy_api_client: ^0.16.0
|
lemmy_api_client: ^0.17.0
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
matrix4_transform: ^2.0.0
|
matrix4_transform: ^2.0.0
|
||||||
json_annotation: ^4.1.0
|
json_annotation: ^4.3.0
|
||||||
keyboard_dismisser: ^2.0.0
|
keyboard_dismisser: ^2.0.0
|
||||||
freezed_annotation: ^0.14.3
|
freezed_annotation: ^0.15.0
|
||||||
logging: ^1.0.1
|
logging: ^1.0.1
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
@ -70,7 +70,7 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_launcher_icons: ^0.9.2
|
flutter_launcher_icons: ^0.9.2
|
||||||
json_serializable: ^5.0.0
|
json_serializable: ^6.0.0
|
||||||
build_runner: ^2.1.2
|
build_runner: ^2.1.2
|
||||||
mobx_codegen: ^2.0.2
|
mobx_codegen: ^2.0.2
|
||||||
freezed: ^0.14.1+2
|
freezed: ^0.14.1+2
|
||||||
|
|
Loading…
Reference in New Issue