diff --git a/lib/src/api/moderation.dart b/lib/src/api/moderation.dart index 3d57208e..b645e251 100644 --- a/lib/src/api/moderation.dart +++ b/lib/src/api/moderation.dart @@ -355,7 +355,21 @@ class APIModeration { }, langCodeIdPairs: await client.languageCodeIdPairs()); case ServerSoftware.piefed: - throw UnimplementedError('Not yet implemented for PieFed'); + const path = '/modlog'; + final query = { + if (communityId != null) 'community_id': communityId.toString(), + if (userId != null) 'mod_person_id': userId.toString(), + 'page': page, + 'type_': type.toLemmy, + }; + final response = await client.get(path, queryParams: query); + final json = response.bodyJson; + return ModlogListModel.fromPiefed({ + 'next_page': + (int.parse(((page?.isNotEmpty ?? false) ? page : '0') ?? '0') + 1) + .toString(), + ...json, + }, langCodeIdPairs: await client.languageCodeIdPairs()); } } } diff --git a/lib/src/models/comment.dart b/lib/src/models/comment.dart index 8abba2fa..f99b8077 100644 --- a/lib/src/models/comment.dart +++ b/lib/src/models/comment.dart @@ -249,7 +249,7 @@ abstract class CommentModel with _$CommentModel { int? postId, }) { final piefedComment = json['comment']! as JsonMap; - final piefedCounts = json['counts']! as JsonMap; + final piefedCounts = json['counts'] as JsonMap?; final piefedPath = piefedComment['path']! as String; final piefedPathSegments = piefedPath.split('.').map(int.parse).toList(); @@ -310,15 +310,15 @@ abstract class CommentModel with _$CommentModel { .firstOrNull ?.$1, isLocked: piefedComment['locked']! as bool, - upvotes: piefedCounts['upvotes']! as int, - downvotes: piefedCounts['downvotes']! as int, + upvotes: piefedCounts?['upvotes'] as int? ?? 0, + downvotes: piefedCounts?['downvotes'] as int? ?? 0, boosts: null, myVote: json['my_vote'] as int?, myBoost: null, createdAt: DateTime.parse(piefedComment['published']! as String), editedAt: optionalDateTime(json['updated'] as String?), children: children, - childCount: piefedCounts['child_count']! as int, + childCount: piefedCounts?['child_count'] as int? ?? 0, visibility: 'visible', canAuthUserModerate: json['can_auth_user_moderate'] as bool?, notificationControlStatus: json['activity_alert'] == null @@ -328,7 +328,7 @@ abstract class CommentModel with _$CommentModel { : NotificationControlStatus.default_, bookmarks: [ // Empty string indicates comment is saved. No string indicates comment is not saved. - if (json['saved']! as bool) '', + if (((json['saved'] as bool?) != null) && json['saved']! as bool) '', ], apId: piefedComment['ap_id']! as String, emojiReactions: diff --git a/lib/src/models/modlog.dart b/lib/src/models/modlog.dart index ff21d5a6..d4509cbf 100644 --- a/lib/src/models/modlog.dart +++ b/lib/src/models/modlog.dart @@ -14,7 +14,7 @@ abstract class ModlogItemModel with _$ModlogItemModel { required ModLogType type, required DateTime createdAt, required String? reason, - required CommunityModel community, + required CommunityModel? community, required DetailedUserModel? moderator, required int? postId, required String? postTitle, @@ -188,6 +188,61 @@ abstract class ModlogItemModel with _$ModlogItemModel { }, ); } + + factory ModlogItemModel.fromPiefed( + JsonMap json, { + required List<(String, int)> langCodeIdPairs, + }) { + final type = ModLogType.values.byName(json['type']! as String); + + return ModlogItemModel( + type: type, + createdAt: DateTime.parse(json['createdAt']! as String), + reason: json['reason'] as String?, + community: json['community'] != null + ? CommunityModel.fromPiefed(json['community']! as JsonMap) + : null, + moderator: json['moderator'] != null + ? DetailedUserModel.fromPiefed(json['moderator']! as JsonMap) + : null, + postId: (json['post'] as JsonMap?)?['id'] as int?, + postTitle: (json['post'] as JsonMap?)?['name'] as String?, + comment: json['comment'] != null && json['creator'] != null + ? CommentModel.fromPiefed(json, langCodeIdPairs: langCodeIdPairs) + : null, + user: json['banned_person'] == null + ? null + : switch (type) { + ModLogType.all => null, + ModLogType.postDeleted => null, + ModLogType.postRestored => null, + ModLogType.commentDeleted => null, + ModLogType.commentRestored => null, + ModLogType.postPinned => null, + ModLogType.postUnpinned => null, + ModLogType.microblogPostDeleted => null, + ModLogType.microblogPostRestored => null, + ModLogType.microblogCommentDeleted => null, + ModLogType.microblogCommentRestored => null, + ModLogType.ban => DetailedUserModel.fromPiefed( + json['banned_person']! as JsonMap, + ), + ModLogType.unban => DetailedUserModel.fromPiefed( + json['banned_person']! as JsonMap, + ), + ModLogType.moderatorAdded => DetailedUserModel.fromPiefed( + json['modded_person']! as JsonMap, + ), + ModLogType.moderatorRemoved => DetailedUserModel.fromPiefed( + json['modded_person']! as JsonMap, + ), + ModLogType.communityAdded => null, + ModLogType.communityRemoved => null, + ModLogType.postLocked => null, + ModLogType.postUnlocked => null, + }, + ); + } } @freezed @@ -316,4 +371,119 @@ abstract class ModlogListModel with _$ModlogListModel { nextPage: items.isNotEmpty ? json['next_page'] as String? : null, ); } + + factory ModlogListModel.fromPiefed( + JsonMap json, { + required List<(String, int)> langCodeIdPairs, + }) { + final removedPosts = (json['removed_posts']! as List) + .map( + (item) => ModlogItemModel.fromPiefed({ + 'type': (item['mod_remove_post'] as JsonMap)['removed']! as bool + ? ModLogType.postDeleted.name + : ModLogType.postRestored.name, + 'createdAt': + (item['mod_remove_post'] as JsonMap)['when_']! as String, + 'reason': (item['mod_remove_post'] as JsonMap)['reason'] as String?, + ...item, + }, langCodeIdPairs: langCodeIdPairs), + ) + .toList(); + + final lockedPosts = (json['locked_posts']! as List).map( + (item) => ModlogItemModel.fromPiefed({ + 'type': (item['mod_lock_post'] as JsonMap)['locked']! as bool + ? ModLogType.postLocked.name + : ModLogType.postUnlocked.name, + 'createdAt': (item['mod_lock_post'] as JsonMap)['when_']! as String, + ...item, + }, langCodeIdPairs: langCodeIdPairs), + ); + + final featuredPosts = (json['featured_posts']! as List).map( + (item) => ModlogItemModel.fromPiefed({ + 'type': (item['mod_feature_post'] as JsonMap)['featured']! as bool + ? ModLogType.postPinned.name + : ModLogType.postUnpinned.name, + 'createdAt': (item['mod_feature_post'] as JsonMap)['when_']! as String, + ...item, + }, langCodeIdPairs: langCodeIdPairs), + ); + + final removedComments = (json['removed_comments']! as List).map( + (item) => ModlogItemModel.fromPiefed({ + 'type': (item['mod_remove_comment'] as JsonMap)['removed']! as bool + ? ModLogType.commentDeleted.name + : ModLogType.commentRestored.name, + 'createdAt': + (item['mod_remove_comment'] as JsonMap)['when_']! as String, + 'reason': (item['mod_remove_comment'] as JsonMap)['reason'] as String?, + ...item as JsonMap, + 'creator': ?(item['commenter'] != null + ? {'person': item['commenter']! as JsonMap} + : null), + }, langCodeIdPairs: langCodeIdPairs), + ); + + final removedCommunities = (json['removed_communities']! as List) + .map( + (item) => ModlogItemModel.fromPiefed({ + 'type': + (item['mod_remove_community'] as JsonMap)['removed']! as bool + ? ModLogType.communityRemoved.name + : ModLogType.communityAdded.name, + 'createdAt': + (item['mod_remove_community'] as JsonMap)['when_']! as String, + ...item as JsonMap, + }, langCodeIdPairs: langCodeIdPairs), + ); + + final modBannedCommunity = (json['banned_from_community']! as List) + .map( + (item) => ModlogItemModel.fromPiefed({ + 'type': + (item['mod_ban_from_community'] as JsonMap)['banned']! as bool + ? ModLogType.ban.name + : ModLogType.unban.name, + 'createdAt': + (item['mod_ban_from_community'] as JsonMap)['when_']! as String, + ...item, + 'reason': + (item['mod_ban_from_community'] as JsonMap)['reason'] + as String?, + 'expires': + (item['mod_ban_from_community'] as JsonMap)['expires'] + as String?, + }, langCodeIdPairs: langCodeIdPairs), + ); + + final modAddedToCommunity = (json['added_to_community']! as List) + .map( + (item) => ModlogItemModel.fromPiefed({ + 'type': (item['mod_add_community'] as JsonMap)['removed']! as bool + ? ModLogType.moderatorRemoved.name + : ModLogType.moderatorAdded.name, + 'createdAt': + (item['mod_add_community'] as JsonMap)['when_']! as String, + ...item, + 'expires': + (item['mod_add_community'] as JsonMap)['expires'] as String?, + }, langCodeIdPairs: langCodeIdPairs), + ); + + final items = [ + ...removedPosts, + ...lockedPosts, + ...featuredPosts, + ...removedComments, + ...removedCommunities, + ...modBannedCommunity, + ...modAddedToCommunity, + ]..sort((a, b) => b.createdAt.compareTo(a.createdAt)); + + return ModlogListModel( + items: items, + nextPage: items.isNotEmpty ? json['next_page'] as String? : null, + ); + } } diff --git a/lib/src/screens/explore/mod_log.dart b/lib/src/screens/explore/mod_log.dart index 68f690e6..c97b3ad1 100644 --- a/lib/src/screens/explore/mod_log.dart +++ b/lib/src/screens/explore/mod_log.dart @@ -81,7 +81,10 @@ class _ModLogScreenState extends State { // Lemmy API returns both positive and negative mod action types for each filter type. // e.g. passing PinnedPost to the API returns both pinned and unpinned actions. // So we do a little extra filtering here to narrow it down further. - ServerSoftware.piefed => throw UnimplementedError(), + ServerSoftware.piefed => + _filter != ModLogType.all + ? newPage.items.where((item) => item.type == _filter).toList() + : newPage.items, }; return (newItems, newPage.nextPage); @@ -96,7 +99,7 @@ class _ModLogScreenState extends State { ? null : () => pushPostPage( context, - communityName: item.community.name, + communityName: item.community?.name, postId: item.postId, postType: PostType.thread, ), @@ -105,7 +108,7 @@ class _ModLogScreenState extends State { ? null : () => pushPostPage( context, - communityName: item.community.name, + communityName: item.community?.name, postId: item.postId, postType: PostType.thread, ), @@ -132,7 +135,7 @@ class _ModLogScreenState extends State { ? null : () => pushPostPage( context, - communityName: item.community.name, + communityName: item.community?.name, postId: item.postId, postType: PostType.thread, ), @@ -141,7 +144,7 @@ class _ModLogScreenState extends State { ? null : () => pushPostPage( context, - communityName: item.community.name, + communityName: item.community?.name, postId: item.postId, postType: PostType.thread, ), @@ -150,7 +153,7 @@ class _ModLogScreenState extends State { ? null : () => pushPostPage( context, - communityName: item.community.name, + communityName: item.community?.name, postId: item.postId, postType: PostType.thread, ), @@ -159,7 +162,7 @@ class _ModLogScreenState extends State { ? null : () => pushPostPage( context, - communityName: item.community.name, + communityName: item.community?.name, postId: item.postId, postType: PostType.thread, ), @@ -205,24 +208,30 @@ class _ModLogScreenState extends State { : () => context.router.push( UserRoute(username: item.user!.name, userId: item.user!.id), ), - ModLogType.communityAdded => () => context.router.push( - CommunityRoute( - communityName: item.community.name, - communityId: item.community.id, - ), - ), - ModLogType.communityRemoved => () => context.router.push( - CommunityRoute( - communityName: item.community.name, - communityId: item.community.id, - ), - ), + ModLogType.communityAdded => + item.community == null + ? null + : () => context.router.push( + CommunityRoute( + communityName: item.community!.name, + communityId: item.community!.id, + ), + ), + ModLogType.communityRemoved => + item.community == null + ? null + : () => context.router.push( + CommunityRoute( + communityName: item.community!.name, + communityId: item.community!.id, + ), + ), ModLogType.postLocked => item.postId == null ? null : () => pushPostPage( context, - communityName: item.community.name, + communityName: item.community?.name, postId: item.postId, postType: PostType.thread, ), @@ -231,7 +240,7 @@ class _ModLogScreenState extends State { ? null : () => pushPostPage( context, - communityName: item.community.name, + communityName: item.community?.name, postId: item.postId, postType: PostType.thread, ), diff --git a/lib/src/screens/settings/about_screen.dart b/lib/src/screens/settings/about_screen.dart index c907e1e9..c4dd6b60 100644 --- a/lib/src/screens/settings/about_screen.dart +++ b/lib/src/screens/settings/about_screen.dart @@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:interstellar/src/controller/controller.dart'; import 'package:interstellar/src/controller/router.gr.dart'; -import 'package:interstellar/src/controller/server.dart'; import 'package:interstellar/src/utils/globals.dart'; import 'package:interstellar/src/utils/utils.dart'; import 'package:interstellar/src/widgets/open_webpage.dart'; @@ -41,13 +40,11 @@ class _AboutScreenState extends State { title: Text(l(context).settings_debug), onTap: () => context.router.push(const DebugSettingsRoute()), ), - if (context.read().serverSoftware != - ServerSoftware.piefed) - ListTile( - leading: const Icon(Symbols.shield_rounded), - title: Text(l(context).modlog), - onTap: () => context.router.push(ModLogRoute()), - ), + ListTile( + leading: const Icon(Symbols.shield_rounded), + title: Text(l(context).modlog), + onTap: () => context.router.push(ModLogRoute()), + ), ListTile( leading: const Icon(Symbols.favorite_rounded), title: Text(l(context).settings_donate), diff --git a/lib/src/widgets/menus/community_menu.dart b/lib/src/widgets/menus/community_menu.dart index 09905a51..631b4611 100644 --- a/lib/src/widgets/menus/community_menu.dart +++ b/lib/src/widgets/menus/community_menu.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:interstellar/src/api/feed_source.dart'; import 'package:interstellar/src/controller/controller.dart'; import 'package:interstellar/src/controller/router.gr.dart'; -import 'package:interstellar/src/controller/server.dart'; import 'package:interstellar/src/models/community.dart'; import 'package:interstellar/src/screens/explore/explore_screen.dart'; import 'package:interstellar/src/screens/explore/user_item.dart'; @@ -162,16 +161,16 @@ Future showCommunityMenu( ), ), ), - if (ac.serverSoftware != ServerSoftware.piefed) - ContextMenuItem( - title: l(context).modlog, - onTap: () => context.router.push( - ModLogCommunityRoute( - communityName: detailedCommunity?.name ?? community!.name, - communityId: detailedCommunity?.id ?? community!.id, - ), + // if (ac.serverSoftware != ServerSoftware.piefed) + ContextMenuItem( + title: l(context).modlog, + onTap: () => context.router.push( + ModLogCommunityRoute( + communityName: detailedCommunity?.name ?? community!.name, + communityId: detailedCommunity?.id ?? community!.id, ), ), + ), ], ).openMenu(context); } diff --git a/lib/src/widgets/menus/user_menu.dart b/lib/src/widgets/menus/user_menu.dart index 98ab47d0..93c02d27 100644 --- a/lib/src/widgets/menus/user_menu.dart +++ b/lib/src/widgets/menus/user_menu.dart @@ -124,7 +124,7 @@ Future showUserMenu( ), ), ), - if (ac.serverSoftware == ServerSoftware.lemmy) + if (ac.serverSoftware != ServerSoftware.mbin) ContextMenuItem( title: l(context).modlog, onTap: () => context.router.push(