From 781ff2553d37c6dd6ea5862fb74b3e610e6a4213 Mon Sep 17 00:00:00 2001 From: nyne <67669799+wgh136@users.noreply.github.com> Date: Sat, 29 Nov 2025 15:04:51 +0800 Subject: [PATCH] Merge pull request #649 from venera-app/feat/comment-blocking feat: add comment keyword blocking functionality --- assets/translation.json | 2 + lib/foundation/appdata.dart | 1 + .../comic_details_page/comments_page.dart | 19 +++- .../comic_details_page/comments_preview.dart | 2 +- lib/pages/reader/chapter_comments.dart | 19 +++- lib/pages/settings/explore_settings.dart | 93 +++++++++++++++++++ 6 files changed, 131 insertions(+), 5 deletions(-) diff --git a/assets/translation.json b/assets/translation.json index 652b60d..aa57b9b 100644 --- a/assets/translation.json +++ b/assets/translation.json @@ -103,6 +103,7 @@ "Show favorite status on comic tile": "在漫画缩略图上显示收藏状态", "Show history on comic tile": "在漫画缩略图上显示历史记录", "Keyword blocking": "关键词屏蔽", + "Comment keyword blocking": "评论关键词屏蔽", "Tap to turn Pages": "点击翻页", "Page animation": "页面动画", "Reading mode": "阅读模式", @@ -529,6 +530,7 @@ "Show favorite status on comic tile": "在漫畫縮圖上顯示收藏狀態", "Show history on comic tile": "在漫畫縮圖上顯示歷史記錄", "Keyword blocking": "關鍵字封鎖", + "Comment keyword blocking": "評論關鍵字封鎖", "Tap to turn Pages": "點擊翻頁", "Page animation": "頁面動畫", "Reading mode": "閱讀模式", diff --git a/lib/foundation/appdata.dart b/lib/foundation/appdata.dart index c6842f5..a5833dd 100644 --- a/lib/foundation/appdata.dart +++ b/lib/foundation/appdata.dart @@ -180,6 +180,7 @@ class Settings with ChangeNotifier { 'showFavoriteStatusOnTile': true, 'showHistoryStatusOnTile': false, 'blockedWords': [], + 'blockedCommentWords': [], 'defaultSearchTarget': null, 'autoPageTurningInterval': 5, // in seconds 'readerMode': 'galleryLeftToRight', // values of [ReaderMode] diff --git a/lib/pages/comic_details_page/comments_page.dart b/lib/pages/comic_details_page/comments_page.dart index 5772929..8b73142 100644 --- a/lib/pages/comic_details_page/comments_page.dart +++ b/lib/pages/comic_details_page/comments_page.dart @@ -1,5 +1,18 @@ part of 'comic_page.dart'; +bool _shouldBlockComment(Comment comment) { + var blockedWords = appdata.settings["blockedCommentWords"] as List; + if (blockedWords.isEmpty) return false; + + var content = comment.content.toLowerCase(); + for (var word in blockedWords) { + if (content.contains(word.toString().toLowerCase())) { + return true; + } + } + return false; +} + class CommentsPage extends StatefulWidget { const CommentsPage({ super.key, @@ -36,8 +49,9 @@ class _CommentsPageState extends State { _loading = false; }); } else if (mounted) { + var filteredComments = res.data.where((c) => !_shouldBlockComment(c)).toList(); setState(() { - _comments = res.data; + _comments = filteredComments; _loading = false; maxPage = res.subData; }); @@ -54,8 +68,9 @@ class _CommentsPageState extends State { if (res.error) { context.showMessage(message: res.errorMessage ?? "Unknown Error"); } else { + var filteredComments = res.data.where((c) => !_shouldBlockComment(c)).toList(); setState(() { - _comments!.addAll(res.data); + _comments!.addAll(filteredComments); _page++; if (maxPage == null && res.data.isEmpty) { maxPage = _page; diff --git a/lib/pages/comic_details_page/comments_preview.dart b/lib/pages/comic_details_page/comments_preview.dart index f6fc60e..0aeaeea 100644 --- a/lib/pages/comic_details_page/comments_preview.dart +++ b/lib/pages/comic_details_page/comments_preview.dart @@ -21,7 +21,7 @@ class _CommentsPartState extends State<_CommentsPart> { @override void initState() { - comments = widget.comments; + comments = widget.comments.where((c) => !_shouldBlockComment(c)).toList(); super.initState(); } diff --git a/lib/pages/reader/chapter_comments.dart b/lib/pages/reader/chapter_comments.dart index 27dab10..eef8db2 100644 --- a/lib/pages/reader/chapter_comments.dart +++ b/lib/pages/reader/chapter_comments.dart @@ -1,5 +1,18 @@ part of 'reader.dart'; +bool _shouldBlockComment(Comment comment) { + var blockedWords = appdata.settings["blockedCommentWords"] as List; + if (blockedWords.isEmpty) return false; + + var content = comment.content.toLowerCase(); + for (var word in blockedWords) { + if (content.contains(word.toString().toLowerCase())) { + return true; + } + } + return false; +} + class ChapterCommentsPage extends StatefulWidget { const ChapterCommentsPage({ super.key, @@ -44,8 +57,9 @@ class _ChapterCommentsPageState extends State { _loading = false; }); } else if (mounted) { + var filteredComments = res.data.where((c) => !_shouldBlockComment(c)).toList(); setState(() { - _comments = res.data; + _comments = filteredComments; _loading = false; maxPage = res.subData; }); @@ -62,8 +76,9 @@ class _ChapterCommentsPageState extends State { if (res.error) { context.showMessage(message: res.errorMessage ?? "Unknown Error"); } else { + var filteredComments = res.data.where((c) => !_shouldBlockComment(c)).toList(); setState(() { - _comments!.addAll(res.data); + _comments!.addAll(filteredComments); _page++; if (maxPage == null && res.data.isEmpty) { maxPage = _page; diff --git a/lib/pages/settings/explore_settings.dart b/lib/pages/settings/explore_settings.dart index a0e1279..adbadcc 100644 --- a/lib/pages/settings/explore_settings.dart +++ b/lib/pages/settings/explore_settings.dart @@ -60,6 +60,10 @@ class _ExploreSettingsState extends State { title: "Keyword blocking".tl, builder: () => const _ManageBlockingWordView(), ).toSliver(), + _PopupWindowSetting( + title: "Comment keyword blocking".tl, + builder: () => const _ManageBlockingCommentWordView(), + ).toSliver(), SelectSetting( title: "Default Search Target".tl, settingKey: "defaultSearchTarget", @@ -250,4 +254,93 @@ Widget setSearchSourcesWidget() { settingsIndex: "searchSources", pages: pages, ); +} + +class _ManageBlockingCommentWordView extends StatefulWidget { + const _ManageBlockingCommentWordView(); + + @override + State<_ManageBlockingCommentWordView> createState() => + _ManageBlockingCommentWordViewState(); +} + +class _ManageBlockingCommentWordViewState extends State<_ManageBlockingCommentWordView> { + @override + Widget build(BuildContext context) { + assert(appdata.settings["blockedCommentWords"] is List); + return PopUpWidgetScaffold( + title: "Comment keyword blocking".tl, + tailing: [ + TextButton.icon( + icon: const Icon(Icons.add), + label: Text("Add".tl), + onPressed: add, + ), + ], + body: ListView.builder( + itemCount: appdata.settings["blockedCommentWords"].length, + itemBuilder: (context, index) { + return ListTile( + title: Text(appdata.settings["blockedCommentWords"][index]), + trailing: IconButton( + icon: const Icon(Icons.close), + onPressed: () { + appdata.settings["blockedCommentWords"].removeAt(index); + appdata.saveData(); + setState(() {}); + }, + ), + ); + }, + ), + ); + } + + void add() { + showDialog( + context: App.rootContext, + builder: (context) { + var controller = TextEditingController(); + String? error; + return StatefulBuilder(builder: (context, setState) { + return ContentDialog( + title: "Add keyword".tl, + content: TextField( + controller: controller, + decoration: InputDecoration( + border: const OutlineInputBorder(), + label: Text("Keyword".tl), + errorText: error, + ), + onChanged: (s) { + if (error != null) { + setState(() { + error = null; + }); + } + }, + ).paddingHorizontal(12), + actions: [ + Button.filled( + onPressed: () { + if (appdata.settings["blockedCommentWords"] + .contains(controller.text)) { + setState(() { + error = "Keyword already exists".tl; + }); + return; + } + appdata.settings["blockedCommentWords"].add(controller.text); + appdata.saveData(); + this.setState(() {}); + context.pop(); + }, + child: Text("Add".tl), + ), + ], + ); + }); + }, + ); + } } \ No newline at end of file