Skip to content

Commit

Permalink
feat: support report abuse for more entities (#475)
Browse files Browse the repository at this point in the history
* fix: fix url of local note for reporting abuse

* fix: add description for abuse report

* feat: report gallery post, page and play

* test: add tests for renotes

* fix: pass renoting note to menu button

* feat: report renote

* fix: change color of renote button
  • Loading branch information
poppingmoon authored Nov 22, 2024
1 parent e9048aa commit 8de616d
Show file tree
Hide file tree
Showing 9 changed files with 695 additions and 15 deletions.
46 changes: 46 additions & 0 deletions lib/view/page/gallery/gallery_post_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:misskey_dart/misskey_dart.dart';
import 'package:share_plus/share_plus.dart';

import '../../../extension/user_extension.dart';
import '../../../i18n/strings.g.dart';
import '../../../model/account.dart';
import '../../../provider/api/gallery_post_notifier_provider.dart';
import '../../../provider/api/i_notifier_provider.dart';
import '../../../provider/api/misskey_provider.dart';
import '../../../provider/api/user_gallery_posts_notifier_provider.dart';
import '../../../provider/post_notifier_provider.dart';
import '../../../util/copy_text.dart';
import '../../../util/future_with_dialog.dart';
import '../../../util/launch_url.dart';
import '../../dialog/confirmation_dialog.dart';
import '../../dialog/image_gallery_dialog.dart';
import '../../dialog/text_field_dialog.dart';
import '../../widget/ad_widget.dart';
import '../../widget/error_message.dart';
import '../../widget/follow_button.dart';
Expand All @@ -37,6 +42,7 @@ class GalleryPostPage extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final i = ref.watch(iNotifierProvider(account)).valueOrNull;
final post = ref.watch(galleryPostNotifierProvider(account, postId));
final url = Uri.https(account.host, 'gallery/$postId');

Expand Down Expand Up @@ -76,6 +82,46 @@ class GalleryPostPage extends ConsumerWidget {
onTap: () => Share.share('${post.valueOrNull?.title} $url'),
child: Text(t.misskey.share),
),
if (post.valueOrNull?.user case final user?
when !account.isGuest && i?.id != user.id)
PopupMenuItem(
onTap: () async {
final comment = await showTextFieldDialog(
context,
title: Text(t.misskey.reportAbuseOf(name: user.acct)),
initialText: [
'Post: $url',
'-----',
'',
].join('\n'),
decoration: InputDecoration(
helperText: t.misskey.fillAbuseReportDescription,
),
maxLines: null,
);
if (!context.mounted) return;
if (comment == null) return;
final confirmed = await confirm(
context,
title: Text(t.misskey.reportAbuseOf(name: user.acct)),
message: comment,
okText: t.misskey.reportAbuse,
);
if (!context.mounted) return;
if (confirmed) {
await futureWithDialog(
context,
ref.read(misskeyProvider(account)).users.reportAbuse(
UsersReportAbuseRequest(
userId: user.id,
comment: comment,
),
),
);
}
},
child: Text(t.misskey.reportAbuse),
),
],
),
],
Expand Down
46 changes: 46 additions & 0 deletions lib/view/page/page/page_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@ import 'package:mfm_parser/mfm_parser.dart';
import 'package:misskey_dart/misskey_dart.dart' hide Clip;
import 'package:share_plus/share_plus.dart';

import '../../../extension/user_extension.dart';
import '../../../gen/fonts.gen.dart';
import '../../../i18n/strings.g.dart';
import '../../../model/account.dart';
import '../../../provider/api/i_notifier_provider.dart';
import '../../../provider/api/misskey_provider.dart';
import '../../../provider/api/page_provider.dart';
import '../../../provider/api/user_pages_notifier_provider.dart';
import '../../../provider/post_notifier_provider.dart';
import '../../../util/copy_text.dart';
import '../../../util/extract_url.dart';
import '../../../util/future_with_dialog.dart';
import '../../../util/launch_url.dart';
import '../../dialog/confirmation_dialog.dart';
import '../../dialog/image_dialog.dart';
import '../../dialog/text_field_dialog.dart';
import '../../widget/ad_widget.dart';
import '../../widget/error_message.dart';
import '../../widget/follow_button.dart';
Expand Down Expand Up @@ -163,6 +168,7 @@ class PagePage extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final i = ref.watch(iNotifierProvider(account)).valueOrNull;
final page = ref.watch(
pageNotifierProvider(
account,
Expand Down Expand Up @@ -206,6 +212,46 @@ class PagePage extends ConsumerWidget {
onTap: () => Share.share('${page.valueOrNull?.title} $url'),
child: Text(t.misskey.share),
),
if (page.valueOrNull?.user case final user?
when !account.isGuest && i?.id != user.id)
PopupMenuItem(
onTap: () async {
final comment = await showTextFieldDialog(
context,
title: Text(t.misskey.reportAbuseOf(name: user.acct)),
initialText: [
'Page: $url',
'-----',
'',
].join('\n'),
decoration: InputDecoration(
helperText: t.misskey.fillAbuseReportDescription,
),
maxLines: null,
);
if (!context.mounted) return;
if (comment == null) return;
final confirmed = await confirm(
context,
title: Text(t.misskey.reportAbuseOf(name: user.acct)),
message: comment,
okText: t.misskey.reportAbuse,
);
if (!context.mounted) return;
if (confirmed) {
await futureWithDialog(
context,
ref.read(misskeyProvider(account)).users.reportAbuse(
UsersReportAbuseRequest(
userId: user.id,
comment: comment,
),
),
);
}
},
child: Text(t.misskey.reportAbuse),
),
],
),
],
Expand Down
51 changes: 51 additions & 0 deletions lib/view/page/play/play_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:misskey_dart/misskey_dart.dart' hide Clip;
import 'package:share_plus/share_plus.dart';

import '../../../extension/user_extension.dart';
import '../../../i18n/strings.g.dart';
import '../../../model/account.dart';
import '../../../provider/api/i_notifier_provider.dart';
import '../../../provider/api/misskey_provider.dart';
import '../../../provider/api/play_notifier_provider.dart';
import '../../../provider/misskey_colors_provider.dart';
import '../../../provider/post_notifier_provider.dart';
import '../../../util/copy_text.dart';
import '../../../util/future_with_dialog.dart';
import '../../../util/launch_url.dart';
import '../../dialog/confirmation_dialog.dart';
import '../../dialog/text_field_dialog.dart';
import '../../widget/ad_widget.dart';
import '../../widget/error_message.dart';
import '../../widget/follow_button.dart';
Expand All @@ -33,6 +40,7 @@ class PlayPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final account = useState(this.account);
final i = ref.watch(iNotifierProvider(account.value)).valueOrNull;
final play = ref.watch(playNotifierProvider(this.account, playId));
final url = Uri.https(this.account.host, 'play/$playId');
final colors =
Expand Down Expand Up @@ -75,6 +83,49 @@ class PlayPage extends HookConsumerWidget {
context.push('/${account.value}/play/$playId/edit'),
child: Text(t.misskey.edit),
),
if (play.valueOrNull?.user case final user?
when !account.value.isGuest && i?.id != user.id)
PopupMenuItem(
onTap: () async {
final comment = await showTextFieldDialog(
context,
title: Text(t.misskey.reportAbuseOf(name: user.acct)),
initialText: [
'Play: $url',
'-----',
'',
].join('\n'),
decoration: InputDecoration(
helperText: t.misskey.fillAbuseReportDescription,
),
maxLines: null,
);
if (!context.mounted) return;
if (comment == null) return;
final confirmed = await confirm(
context,
title: Text(t.misskey.reportAbuseOf(name: user.acct)),
message: comment,
okText: t.misskey.reportAbuse,
);
if (!context.mounted) return;
if (confirmed) {
await futureWithDialog(
context,
ref
.read(misskeyProvider(account.value))
.users
.reportAbuse(
UsersReportAbuseRequest(
userId: user.id,
comment: comment,
),
),
);
}
},
child: Text(t.misskey.reportAbuse),
),
],
),
],
Expand Down
15 changes: 8 additions & 7 deletions lib/view/widget/note_footer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import '../../provider/api/meta_notifier_provider.dart';
import '../../provider/api/misskey_provider.dart';
import '../../provider/appear_note_provider.dart';
import '../../provider/general_settings_notifier_provider.dart';
import '../../provider/misskey_colors_provider.dart';
import '../../provider/note_provider.dart';
import '../../provider/notes_notifier_provider.dart';
import '../../provider/post_notifier_provider.dart';
Expand Down Expand Up @@ -170,7 +171,7 @@ class NoteFooter extends HookConsumerWidget {
),
_MenuButton(
account: account,
note: appearNote,
note: note,
disableHeader: disableHeader,
focusPostForm: focusPostForm,
),
Expand Down Expand Up @@ -250,6 +251,8 @@ class _RenoteButton extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final myRenotingNoteId = useState(this.myRenotingNoteId);
final colors =
ref.watch(misskeyColorsProvider(Theme.of(context).brightness));

return GestureDetector(
onLongPress: note.renoteCount > 0
Expand Down Expand Up @@ -286,8 +289,8 @@ class _RenoteButton extends HookConsumerWidget {
leading: const Icon(Icons.delete),
title: Text(t.misskey.unrenote),
onTap: () => context.pop(true),
iconColor: Theme.of(context).colorScheme.error,
textColor: Theme.of(context).colorScheme.error,
iconColor: colors.error,
textColor: colors.error,
),
],
),
Expand Down Expand Up @@ -342,9 +345,7 @@ class _RenoteButton extends HookConsumerWidget {
children: [
Icon(
Icons.repeat_rounded,
color: myRenotingNoteId.value != null
? Theme.of(context).colorScheme.primary
: null,
color: myRenotingNoteId.value != null ? colors.renote : null,
),
if (note.renoteCount > 0)
Padding(
Expand All @@ -353,7 +354,7 @@ class _RenoteButton extends HookConsumerWidget {
NumberFormat().format(note.renoteCount),
style: style?.apply(
color: myRenotingNoteId.value != null
? Theme.of(context).colorScheme.primary.withOpacity(0.6)
? colors.renote.withOpacity(0.6)
: null,
),
),
Expand Down
56 changes: 53 additions & 3 deletions lib/view/widget/note_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -499,12 +499,62 @@ class NoteSheet extends ConsumerWidget {
t.misskey.reportAbuseOf(name: appearNote.user.acct),
),
initialText: [
if ((appearNote.url ?? appearNote.uri) != null)
'Note: ${appearNote.url ?? appearNote.uri}',
'Local Note: https://${account.host}/${appearNote.id}',
if (appearNote.url ?? appearNote.uri case final url?)
'Note: $url',
'Local Note: $url',
'-----',
'',
].join('\n'),
decoration: InputDecoration(
helperText: t.misskey.fillAbuseReportDescription,
),
maxLines: null,
);
if (!context.mounted) return;
if (comment == null) return;
final confirmed = await confirm(
context,
title: Text(
t.misskey.reportAbuseOf(name: appearNote.user.acct),
),
message: comment,
okText: t.misskey.reportAbuse,
);
if (!context.mounted) return;
if (confirmed) {
await futureWithDialog(
context,
ref.read(misskeyProvider(account)).users.reportAbuse(
UsersReportAbuseRequest(
userId: appearNote.userId,
comment: comment,
),
),
);
}
},
),
if (note.isRenote &&
(note.user.host != null ||
note.user.username != account.username))
ListTile(
leading: const Icon(Icons.report_outlined),
title: Text(t.misskey.reportAbuseRenote),
onTap: () async {
final comment = await showTextFieldDialog(
context,
title: Text(
t.misskey.reportAbuseOf(name: appearNote.user.acct),
),
initialText: [
if (note.uri ?? note.url case final url?) 'Note: $url',
'Local Note: ${Uri.https(account.host, '/notes/$noteId')}',
'-----',
'',
].join('\n'),
decoration: InputDecoration(
helperText: t.misskey.fillAbuseReportDescription,
),
maxLines: null,
);
if (!context.mounted) return;
Expand Down
3 changes: 3 additions & 0 deletions lib/view/widget/user_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,9 @@ class UserSheet extends ConsumerWidget {
final comment = await showTextFieldDialog(
context,
title: Text(t.misskey.reportAbuseOf(name: user.acct)),
decoration: InputDecoration(
helperText: t.misskey.fillAbuseReportDescription,
),
maxLines: null,
);
if (!context.mounted) return;
Expand Down
9 changes: 9 additions & 0 deletions test/flutter_test_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'dart:async';

import 'package:flutter_test/flutter_test.dart';

FutureOr<void> testExecutable(FutureOr<void> Function() testMain) {
WidgetController.hitTestWarningShouldBeFatal = true;

return testMain();
}
Loading

0 comments on commit 8de616d

Please sign in to comment.